SDK Quickstart
Applies to: ChengOS v0.2.0+ | Last verified: 2026-09-25 | Source:
chengflow-sdk/src/sdk,chengflow-sdk/src/hooks/use-channel.ts,chengflow-sdk/package.json,chengflow-sdk/src/sdk/ws-client.ts
The gateway SDK (@chengflow/chat) puts a ChengOS-backed chat interface into a web application.
It comes in three layers, and you pick how much you want:
| Layer | What it gives you | Use when |
|---|---|---|
| Core SDK | Framework-agnostic TypeScript: REST client, session manager, WebSocket client | Vue, Svelte, vanilla, or your own state layer |
| React hooks | useChannel, useWebSocket, useConversation | React, with your own UI |
| React components | ChatProvider, ChatWindow, MessageList, InputBar | React, want it working today |
Read the gateway model first — the four identifiers below only make sense against it.
Install
npm install @chengflow/chat
The published release is @chengflow/[email protected]. It is ESM-only ("type": "module",
import conditions only — no CommonJS build) and needs react and react-dom 18 or newer as
peer dependencies. It has three entry points:
| Import | Contains |
|---|---|
@chengflow/chat | Everything: the core SDK, React hooks, and components |
@chengflow/chat/sdk | The framework-free core only — no React in your bundle |
@chengflow/chat/style.css | Styles for the bundled components; import it once when you use them |
The 0.1.0 package renders assistant replies as Markdown. Math (KaTeX) and the other rich-content formatting in the source tree arrive with the next published release; do not rely on them from npm yet.
Configure
Everything starts from one config object:
import type { ChannelConfig } from "@chengflow/chat";
const config: ChannelConfig = {
apiBaseUrl: "https://your-server.example.com/api/v1",
wsBaseUrl: "wss://your-server.example.com/ws/executions",
workspaceId: "<workspace-uuid>", // required, always explicit
channelId: "weather-app", // where messages come from
boundWorkflowId: "<workflow-uuid>",// what runs
externalUserId: "user-123", // optional
};
workspaceId is required and validated — an empty value raises VALIDATION_ERROR rather than
silently defaulting, because a wrong workspace would mean cross-tenant routing.
Keep channelId and boundWorkflowId distinct. The first identifies the app; the second selects
the workflow.
The fastest path: components
import { ChatProvider, ChatWindow } from "@chengflow/chat";
import "@chengflow/chat/style.css";
export function App() {
return (
<ChatProvider config={config}>
<ChatWindow />
</ChatProvider>
);
}
That is a working chat UI with streaming, history, and approval cards.
Your own UI: the hook
import { useChannel } from "@chengflow/chat";
function Chat() {
const {
messages,
sendMessage,
isLoading,
connectionStatus,
streamingContent,
supportsAttachments,
error,
} = useChannel(config);
return (
<div>
<span>{connectionStatus}</span>
{messages.map((m) => (
<div key={m.id}>{m.content}</div>
))}
{streamingContent && <div>{streamingContent}</div>}
<button disabled={isLoading} onClick={() => sendMessage("Hello")}>
Send
</button>
{error && <p>{error.message}</p>}
</div>
);
}
useChannel runs the whole send-and-await cycle: it creates an optimistic local message, calls
the execute API, subscribes to the conversation and execution scopes, consumes WebSocket events,
and updates message state.
Render streamingContent separately from messages. It holds the partially-received response
while tokens are still arriving; when the turn completes, the finished text lands in messages.
Treating them as one list makes text flicker or duplicate.
supportsAttachments is a capability probe: it reports whether the bound workflow actually
contains an io/file_upload node. Use it to show or hide your attachment button, rather than
offering uploads the workflow will discard.
Human-in-the-loop
Three callbacks handle the cases where a run needs a person:
| Callback | For |
|---|---|
submitApproval(messageId, decision, scope?, reason?) | An approval request — approve or reject, optionally scoped, optionally with a reason |
continueAgentReview(messageId, nextAction?) | An agent paused for review |
submitContextMemoryReview(messageId, updates, trimmingMode) | Context/memory trimming decisions |
These are not optional extras if your workflows use utils/approver or long agent runs — without
them a run that asks for a decision waits indefinitely.
resetConversation() starts a fresh conversation, abandoning the current history.
Core SDK without React
import { ChannelClient, SessionManager, WsClient } from "@chengflow/chat/sdk";
const client = new ChannelClient(config);
const result = await client.execute(/* … */);
ChannelClient also exposes resolveConversation, getConversationMessages, createMessage,
submitApproval, resumeExecution, getExecution, getExecutionResult,
workflowSupportsAttachments, and bulkUpdateMemoryControls.
Authentication
The browser uses a JWT and only a JWT. A BrowserAuthSession supplies the token by default;
pass your own AuthTokenProvider to the ChannelClient constructor to integrate an existing
auth system. Expiry is surfaced through an auth-expired notification so you can re-authenticate
instead of failing silently.
Do not use chk_ channel keys from a browser — they are for server-side adapters and would put a
long-lived shared secret in client code. See authentication.
WebSocket behaviour
The bundled client handles reconnection with exponential backoff plus jitter, PING/PONG heartbeats, multi-scope subscriptions, automatic subscription restoration after a reconnect, a maximum retry limit, and close-code interpretation so a deliberate close is not retried like a network failure. You do not need to implement any of that.
The client also opens every connection with a protocol handshake: its first frame declares
the wire protocol version (1.0) and the SDK version. A server that cannot speak that major
version answers PROTOCOL_MISMATCH and closes; the client then stops reconnecting and reports
status protocol_mismatch, so show an "update the SDK" message rather than a spinner. Servers
that predate the handshake ignore the frame, so a newer SDK keeps working against them.
Deployment
Docker deployments configure the gateway through APP_API_BASE_URL, APP_WS_URL,
APP_CHANNEL_ID, APP_BOUND_WORKFLOW_ID, and PUBLIC_APP_URL — see
the environment reference.
Next steps
- Gateway model — the mapping this SDK implements.
- WebSocket API — the events underneath.