# Integration Examples

> Applies to: ChengOS v0.1.0+ ｜ Last verified: 2026-08-12 ｜ Source: `chengflow-sdk/example/App.tsx`, `chengflow-sdk/src/components/index.ts`, `chengflow-sdk/src/components/ChatProvider.tsx`, `chengflow-sdk/src/hooks`

Four integration shapes, from "one component" to "no framework at all". Pick the one that
matches how much of the UI you want to own.

## 1. The whole console, one component

The shipped example is nine lines of real code:

```tsx
import { MultiChannelLayout } from "@chengflow/chat";

const config = {
  apiBaseUrl: import.meta.env.VITE_API_BASE_URL || "/api/v1",
  wsBaseUrl:  import.meta.env.VITE_WS_BASE_URL  || "/ws/executions",
  channelId:  import.meta.env.VITE_CHANNEL_ID   || "weather-app",
  boundWorkflowId: import.meta.env.VITE_BOUND_WORKFLOW_ID || "",
};

export default function App() {
  return <MultiChannelLayout config={config} loginTitle="Chengflow"
                             chatWindowProps={{ height: "100vh" }} />;
}
```

`MultiChannelLayout` is the complete browser gateway: login, workspace selection, channel
management, and chat — the flow described in
[Gateway Model](/docs/sdk-gateway-model). Use it when you want the product, not a component
library.

The example also reads a runtime config object off `window`, falling back to build-time env
vars. That pattern is worth copying: it lets one built bundle be deployed against different
backends by writing a small config script at serve time, instead of rebuilding per environment.

## 2. Just the chat window

```tsx
import { ChatProvider, ChatWindow } from "@chengflow/chat";

<ChatProvider config={{
  apiBaseUrl: "https://api.example.com/api/v1",
  wsBaseUrl:  "wss://api.example.com/ws/executions",
  workspaceId: "…",
  channelId:  "weather-app",
  boundWorkflowId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
}}>
  <ChatWindow />
</ChatProvider>
```

`ChatProvider` runs `useChannel(config)` and injects the result into context; `ChatWindow`
consumes it. Any component under the provider can call `useChatContext()`.

## 3. Your own UI over the hook

When the chat needs to look like your product:

```tsx
import { useChannel } from "@chengflow/chat";

function MyChat({ config }) {
  const {
    messages, sendMessage, isLoading, connectionStatus,
    streamingContent, resetConversation, error, supportsAttachments,
  } = useChannel(config);

  return (
    <div>
      <StatusDot status={connectionStatus} />
      {messages.map(m => <MyBubble key={m.id} message={m} />)}
      {streamingContent && <MyBubble message={{ content: streamingContent }} streaming />}
      <MyInput onSend={sendMessage} disabled={isLoading}
               allowFiles={supportsAttachments} />
    </div>
  );
}
```

`useChannel` also returns `submitApproval`, `continueAgentReview`, and
`submitContextMemoryReview`. If your workflow contains an
[approval gate](/docs/exec-approvals-reviews), you must render something for it — otherwise the
execution sits in `waiting_for_review` with no way for the user to answer.

`supportsAttachments` is derived by asking whether the bound workflow contains an
`io/file_upload` node, so the file button appears only where uploads actually go somewhere.

## 4. No framework

The core classes are plain TypeScript:

```ts
import { ChannelClient, SessionManager, WsClient } from "@chengflow/chat/sdk";

const sessions = new SessionManager("weather-app");
const client   = new ChannelClient(config);
const ws       = new WsClient({ url: config.wsBaseUrl, tokenProvider: getToken });

ws.connect();

const sessionId = sessions.getOrCreateSessionId();
const { conversation_id, execution_id } =
  await client.execute("weather-app", workflowId, "What is the weather?", sessionId);

sessions.setConversationId(conversation_id);
ws.send({ type: "SUBSCRIBE", scope: { type: "conversation", conversationId: conversation_id } });
if (execution_id) {
  ws.send({ type: "SUBSCRIBE", scope: { type: "execution", executionId: execution_id } });
}
```

Note the `if (execution_id)`: it can be `null` when the message did not trigger a workflow.

## Pairing external platforms

To let users connect their own Telegram bot or Slack app from your UI, the SDK ships a pairing
form and an edit panel per platform — Telegram, WhatsApp, Slack, WeCom, DingTalk — plus
`AppLinkPlatformModals` to present them together.

The generic path is better than picking a form by hand:

```ts
const pattern = await management.getChannelAuthPattern(workspaceId, channelId);
// render fields from `pattern`
const result = await management.connectChannel(workspaceId, channelId, values);
if (result.state === "configuring") {
  // OAuth redirect or QR in setup_data → then completeConnect(...)
}
```

`getChannelAuthPattern` returns the fields the platform requires, so one generic form covers
every platform and a new adapter needs no frontend change. See
[Messaging Channels](/docs/chat-messaging-channels) for what each pattern means.

## Building a management console

```tsx
import { ManagementClient, ChannelList, CreateChannelModal, AppShell } from "@chengflow/chat";
```

`listChannels`, `createChannel`, `listWorkspaces`, and `listPublishedWorkflows` cover a channel
admin screen. `AppShell` and `ChannelList` give you the layout if you do not want to build it.

## Auth

`LoginPage` and `ResetPasswordPage` are drop-in. The example routes `/reset-password` by reading
the `token` query parameter:

```tsx
if (window.location.pathname === "/reset-password") {
  const token = new URLSearchParams(window.location.search).get("token");
  return <ResetPasswordPage apiBaseUrl={config.apiBaseUrl} token={token} />;
}
```

If your application already handles auth, implement `AuthTokenProvider` and pass it to
`ChannelClient` instead of using `BrowserAuthSession`.

## i18n

`I18nProvider`, `useI18n`, and `setLocale` are exported from the package root — not from
`components` — so import them from `@chengflow/chat` directly.

## Choosing

| You want | Use |
|---|---|
| A working product now | `MultiChannelLayout` |
| Chat inside your app's shell | `ChatProvider` + `ChatWindow` |
| Your own chat UI | `useChannel` |
| Non-React, or a server | `ChannelClient` + `WsClient` + `SessionManager` |

## Next steps

- [SDK API Reference](/docs/sdk-api-reference) — every class and method.
- [SDK Quickstart](/docs/sdk-quickstart) — from zero to a first message.
- [Channel Routing & Session Mapping](/docs/chat-channel-routing) — what the session id means.
