# Channels: Web UI, Slack, Matrix, and custom adapters This document explains how **channels** work in the gateway and how to integrate transports like Web UI, Slack, Matrix, or your own chat source. --- ## What is a "channel" in this project? In this gateway, a channel is not a separate server object you provision. A channel is simply an upstream conversation context (for example: a Slack thread, a Matrix room thread, or a browser chat tab) that maps to one stable `conversationId`. The gateway uses that `conversationId` to keep one long-lived agent session. --- ## How channels are represented internally You have two integration options: 1. **Direct chat endpoints** - `POST /v1/chat` - `POST /v1/chat/stream` - You provide `conversationId` directly. 2. **Adapter endpoints** (recommended for Slack/Matrix/etc.) - `POST /v1/adapters/chat` - `POST /v1/adapters/chat/stream` - You provide channel/thread fields; the gateway derives `conversationId` for you. Adapter ID format: - `conversationId = source:workspaceId:channelId:threadId` - `adapterKey = source:workspaceId:channelId:threadId:userId` Field behavior on adapter routes: - `source` (optional, default: `generic`) - `workspaceId` (optional, default: `default`) - `channelId` (**required**) - `threadId` (optional, default: `root`) - `userId` (optional, default: `anonymous`) Important constraints: - segment values must be strings - `channelId` must not be empty - segment values must not contain `:` If your upstream IDs contain `:` (common in Matrix), sanitize/encode them before sending to adapter routes. --- ## Do I need to "create" a channel first? Usually: **no**. A conversation/session is auto-created on first message. You can optionally pre-create one via: - `POST /v1/conversations` But for most channel integrations, you just start sending messages to chat endpoints with stable IDs. --- ## Requirements checklist (all channels) 1. Gateway is running (`RUN_MODE=gateway`) 2. Model/provider credentials are configured (`PROVIDER`, `MODEL`, `API_KEY` or provider-specific key) 3. Your adapter/client can reach the gateway URL 4. If `GATEWAY_AUTH_TOKEN` is set, send `Authorization: Bearer ` 5. For browser apps on another origin, set `GATEWAY_CORS_ORIGIN` 6. If you want restart-safe sessions, set `SESSION_PERSIST=true` --- ## Web UI channel ### Built-in UI The built-in UI is available at `GET /` when: - `GATEWAY_ENABLE_WEB_UI=true` How it works: - sends messages to `/v1/chat/stream` - stores `conversationId` in browser local storage - "New session" clears local `conversationId` and starts a new thread ### Custom web app If you build your own web frontend, call `/v1/chat` or `/v1/chat/stream` and choose your own ID format, e.g.: - `web::` Example: ```json { "conversationId": "web:user-42:chat-main", "message": "Summarize the last response" } ``` --- ## Slack channel integration There is no built-in Slack bot in this repository; you run a small Slack adapter service. Typical adapter needs: - Slack bot token + app configuration (events/interactions) - webhook/event receiver in your adapter - code that forwards messages to gateway and posts replies back to Slack Recommended mapping from Slack event payload: - `source`: `"slack"` - `workspaceId`: Slack team/workspace ID (e.g. `T123`) - `channelId`: Slack channel ID (e.g. `C456`) **required** - `threadId`: Slack `thread_ts` (or fallback strategy) - `userId`: Slack user ID (e.g. `U789`) Example request to gateway: ```bash curl -N -X POST http://localhost:8787/v1/adapters/chat/stream \ -H 'content-type: application/json' \ -d '{ "source": "slack", "workspaceId": "T123", "channelId": "C456", "threadId": "1712233.991", "userId": "U789", "message": "Please summarize this thread" }' ``` Threading strategy tip: - use a stable `threadId` per Slack thread to keep context isolated per thread - if you omit `threadId`, gateway uses `root` (all messages for that channel collapse into one thread context) --- ## Matrix channel integration There is no built-in Matrix bot in this repository; use a Matrix bot/bridge process as adapter. Typical adapter needs: - Matrix bot account + access token - event listener for room messages - logic to send assistant output back into room/thread Recommended mapping: - `source`: `"matrix"` - `workspaceId`: homeserver or deployment identifier (optional but useful) - `channelId`: Matrix `room_id` (**required**, encoded/sanitized for adapter route) - `threadId`: Matrix thread root event ID (encoded/sanitized, or omit for `root`) - `userId`: Matrix sender ID (encoded/sanitized) Because adapter segments cannot contain `:`, Matrix IDs should be encoded in the adapter, e.g.: ```ts const encodeSegment = (value: string) => encodeURIComponent(value); ``` Example payload (encoded values): ```json { "source": "matrix", "workspaceId": "matrix.org", "channelId": "%21roomid%3Amatrix.org", "threadId": "%24rootEventId%3Amatrix.org", "userId": "%40alice%3Amatrix.org", "message": "What did we decide in this thread?" } ``` Alternative: call `/v1/chat` directly and provide your own `conversationId` format if you do not want per-segment adapter normalization. --- ## Custom channel template (Discord, Telegram, email, etc.) For any source, map your upstream identifiers into adapter fields and keep that mapping stable. Minimal payload: ```json { "channelId": "your-channel-id", "message": "Hello" } ``` Full payload template: ```json { "source": "custom", "workspaceId": "tenant-a", "channelId": "channel-123", "threadId": "thread-456", "userId": "user-789", "message": "Hello" } ``` --- ## Best practices - Keep `threadId` stable per thread (do not generate random values per message) - Include `userId` for audit/telemetry (`adapterKey`) even though it does not change `conversationId` - Avoid `:` in any adapter segment value - Use streaming endpoints for better UX (`/stream`) - Persist sessions in production (`SESSION_PERSIST=true`) - Add retry/backoff logic in your adapter for network failures --- ## Related docs - Gateway internals/API: `docs/gateway.md` - Built-in browser UI: `docs/web-ui.md`