Channel API
Connect your own messaging product to chat.dev with inbound messages and outbound webhooks.
The Channel API lets any messaging service — Slack, Discord, Telegram, or your own — act as a frontend for chat.dev. Users on your platform can create agents, send tasks, attach files, and receive responses through your interface.
Overview#
A channel is a connection between your messaging platform and chat.dev. When you create a channel, you get:
- An API key for authenticating inbound messages
- A webhook secret for verifying outbound message signatures
- A callback URL where chat.dev delivers agent responses
There are two directions of traffic:
- Inbound: your app calls
POST /api/channels/:channelId/inboundwhen one of your users sends a message, clicks a command button, or uploads an attachment. - Outbound: chat.dev calls your
callbackUrlwhen it needs to send a message back later, such as agent progress, task output, or asynchronous status updates.
The callbackUrl is your HTTPS endpoint for outbound delivery. It should accept a JSON POST, verify the X-Webhook-Signature, then render or deliver the message to the user or group identified by externalUserId and groupExternalId.
Authentication#
Management endpoints#
Channel CRUD operations require a session cookie (same auth as the web dashboard).
Inbound messages#
Pass the API key in the Authorization: Bearer <key> header.
The API key authenticates your integration to chat.dev. It is not a user token and should never be shown to end users.
Outbound webhooks#
chat.dev signs every outbound payload with HMAC-SHA256 using your webhook secret. Verify the X-Webhook-Signature header:
const crypto = require("crypto");
function verifySignature(body, signature, secret) {
const expected = crypto.createHmac("sha256", secret).update(body).digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
Endpoints#
Endpoint index#
| Method | Endpoint | Auth | What it does |
|---|---|---|---|
GET | /api/channels | Session cookie | List channels owned by the logged-in chat.dev user. |
POST | /api/channels | Session cookie | Create a channel owned by the logged-in chat.dev user. |
DELETE | /api/channels/:id | Session cookie | Delete one of the logged-in user's channels. |
POST | /api/channels/:id/rotate-key | Session cookie | Rotate a channel API key and return the new key once. |
POST | /api/channels/register | Registration key in JSON body | Create a text-only channel without a browser session. |
POST | /api/channels/:channelId/inbound | Authorization: Bearer <apiKey> | Send a user message, command text, group message, or attachment into chat.dev. |
POST | /api/channels/:channelId/commands/getlink | Authorization: Bearer <apiKey> | Get a one-click authenticated link to the loaded agent's live terminal page. |
POST | /api/channels/:channelId/commands/getwallet | Authorization: Bearer <apiKey> | Get the wallet public key for a named accessible agent. |
POST | /api/channels/:channelId/commands/help | Authorization: Bearer <apiKey> | Run /help. |
POST | /api/channels/:channelId/commands/status | Authorization: Bearer <apiKey> | Run /status. |
POST | /api/channels/:channelId/commands/yolo-mode | Authorization: Bearer <apiKey> | Run /yolo-mode. |
POST | /api/channels/:channelId/commands/yolo-mode-off | Authorization: Bearer <apiKey> | Run /yolo-mode-off. |
POST | /api/channels/:channelId/commands/mute | Authorization: Bearer <apiKey> | Run /mute. |
POST | /api/channels/:channelId/commands/list | Authorization: Bearer <apiKey> | Run /list. |
POST | /api/channels/:channelId/commands/balance | Authorization: Bearer <apiKey> | Run /balance. |
POST | /api/channels/:channelId/commands/fund | Authorization: Bearer <apiKey> | Run /fund. |
POST | /api/channels/:channelId/commands/login | Authorization: Bearer <apiKey> | Run /login. |
POST | /api/channels/:channelId/commands/logout | Authorization: Bearer <apiKey> | Run /logout. |
POST | /api/channels/:channelId/commands/deload | Authorization: Bearer <apiKey> | Run /deload. |
POST | /api/channels/:channelId/commands/repos | Authorization: Bearer <apiKey> | Run /repos. |
POST | /api/channels/:channelId/commands/create | Authorization: Bearer <apiKey> | Run /create NAME. |
POST | /api/channels/:channelId/commands/spawn | Authorization: Bearer <apiKey> | Run /spawn REPO. |
POST | /api/channels/:channelId/commands/load | Authorization: Bearer <apiKey> | Run /load NAME. |
POST | /api/channels/:channelId/commands/share | Authorization: Bearer <apiKey> | Run /share NAME. |
POST | /api/channels/:channelId/commands/share-agent | Authorization: Bearer <apiKey> | Run /share-agent NAME. |
POST | /api/channels/:channelId/commands/create-shared | Authorization: Bearer <apiKey> | Run /create-shared NAME. |
POST | /api/channels/:channelId/commands/unshare-agent | Authorization: Bearer <apiKey> | Run /unshare-agent NAME. |
POST | /api/channels/:channelId/commands/start | Authorization: Bearer <apiKey> | Run /start or /start NAME. |
POST | /api/channels/:channelId/commands/start-agent | Authorization: Bearer <apiKey> | Run /start_agent or /start_agent NAME. |
POST | /api/channels/:channelId/commands/stop | Authorization: Bearer <apiKey> | Run /stop or /stop NAME. |
POST | /api/channels/:channelId/commands/stop-agent | Authorization: Bearer <apiKey> | Run /stop_agent or /stop_agent NAME. |
POST | /api/channels/:channelId/commands/restart | Authorization: Bearer <apiKey> | Run /restart or /restart NAME. |
POST | /api/channels/:channelId/commands/delete | Authorization: Bearer <apiKey> | Run /delete NAME. |
POST | /api/channels/:channelId/commands/fund-agent | Authorization: Bearer <apiKey> | Run /fund_agent AGENT AMOUNT. |
POST | /api/channels/:channelId/commands/fund_agent | Authorization: Bearer <apiKey> | Alias for /api/channels/:channelId/commands/fund-agent. |
POST | /api/channels/:channelId/commands/fork | Authorization: Bearer <apiKey> | Run /fork NAME REPO. |
POST | /api/channels/:channelId/commands/yolo | Authorization: Bearer <apiKey> | Run /yolo META-COMMAND. |
POST | /api/channels/:channelId/commands/prompt | Authorization: Bearer <apiKey> | Run /prompt MESSAGE. |
POST | /api/channels/:channelId/actions/get-link | Authorization: Bearer <apiKey> | Compatibility alias for POST /api/channels/:channelId/commands/getlink. |
POST | /api/channels/:channelId/actions/get-wallet | Authorization: Bearer <apiKey> | Compatibility alias for POST /api/channels/:channelId/commands/getwallet. |
chat.dev also sends outbound webhook requests to your channel's callbackUrl. That is not a chat.dev API endpoint; it is your HTTPS endpoint that receives POST requests from chat.dev.
Create a channel#
Use this endpoint from an authenticated chat.dev web session. It creates a channel owned by the logged-in chat.dev user.
POST /api/channels
Content-Type: application/json
Cookie: <session>
{
"name": "My Slack Bot",
"platform": "slack",
"callbackUrl": "https://your-server.com/chat-dev-webhook"
}
Fields:
| Field | Required | Description |
|---|---|---|
name | Yes | Human-readable name for this integration, shown in the owner's channel list. |
platform | Yes | Short platform label such as slack, discord, telegram, or custom. |
callbackUrl | Yes | Your HTTPS webhook endpoint. chat.dev POSTs outbound messages to this URL. |
Response (201):
{
"id": "ch_abc123",
"name": "My Slack Bot",
"platform": "slack",
"callbackUrl": "https://your-server.com/chat-dev-webhook",
"apiKey": "chk_...",
"webhookSecret": "whsec_..."
}
Save the apiKey and webhookSecret immediately — the API key is only shown once.
List channels#
GET /api/channels
Cookie: <session>
Delete a channel#
DELETE /api/channels/:id
Cookie: <session>
Rotate API key#
POST /api/channels/:id/rotate-key
Cookie: <session>
Returns a new apiKey.
Send an inbound message#
The main endpoint your integration calls when a user sends a message.
POST /api/channels/:channelId/inbound
Authorization: Bearer <apiKey>
Content-Type: application/json
{
"externalUserId": "U12345",
"displayName": "Alice",
"message": "/create my-agent",
"messageId": "msg_unique_id",
"isGroup": false,
"groupExternalId": "G67890",
"groupName": "Engineering",
"participants": ["U12345", "U67890"],
"attachments": [
{
"url": "https://your-cdn.example.com/uploads/screenshot.png",
"contentType": "image/png",
"filename": "screenshot.png"
}
]
}
| Field | Required | Description |
|---|---|---|
externalUserId | Yes | Unique user ID on your platform |
message | Yes unless attachments are present | Message text, command text, or button payload. Send /getlink to request an authenticated terminal link for the loaded agent, or call the command endpoint below. |
displayName | No | User's display name |
messageId | No | Unique message ID for deduplication |
isGroup | No | Whether this is a group conversation |
groupExternalId | No | External group/channel ID |
groupName | No | Group display name |
participants | No | List of participant IDs for group sync |
attachments | No | Files or media to save into the loaded agent workspace. Each attachment accepts url, fileUrl, mediaUrl, or attachmentUrl, plus optional contentType/mimeType and filename/name. |
Identity rules:
externalUserIdis scoped to this channel. Use the stable user ID from your platform, not a display name.- For DMs, omit
isGroupor set it tofalse. - For group conversations, set
isGroup: trueand include a stablegroupExternalId. Group members can write@chatdev messageto use their own chat.dev account tools in the shared group, or@agentname messageto talk to one of their own agents or an agent shared into that group. Follow-up messages from the same sender stick to the last@chatdevor@agentnametarget until they mention a different target. participantsis optional. If supplied, chat.dev can sync known group members, but channel users are only auto-created when they send their first message.
Response:
{
"ok": true,
"replies": [
"Agent 'my-agent' created and starting..."
]
}
Synchronous replies appear in the replies array. Async agent output (task results, status updates) is delivered to your callback URL via webhook.
Your integration should display every normal string in replies to the user. A reply prefixed with __structured: contains machine-readable JSON for your backend and usually should not be shown raw in the user interface.
If chat.dev returns a continue-setup link, show it to the user. Channel users can start without browser setup, but once they hit the free agent or prompt/API limits they need to finish setup before continuing.
Channel command endpoints#
All command endpoints use the same Authorization: Bearer <apiKey> header as inbound messages. They expose the slash commands as structured API calls so clients do not have to send command text through /inbound.
Common request body fields for every command endpoint:
| Field | Type | Required | Description |
|---|---|---|---|
externalUserId | string | Yes | Stable user ID from your platform. This scopes the chat.dev user identity inside the channel. |
displayName | string | No | User display name to store or update for this channel identity. |
messageId | string | No | Client-generated idempotency/deduplication ID for this action. |
isGroup | boolean | No | Set to true when the command came from a group conversation. |
groupExternalId | string | Required for group-scoped commands | Stable group/channel/conversation ID from your platform. |
groupName | string | No | Human-readable group name. |
participants | array | No | Group participant IDs or participant objects. Used for group member sync. |
For group commands, include:
{
"externalUserId": "U12345",
"isGroup": true,
"groupExternalId": "G67890",
"groupName": "Engineering"
}
Command endpoints return the same shape as inbound messages unless noted:
{
"ok": true,
"replies": ["..."]
}
| Slash command | API endpoint | Extra body fields |
|---|---|---|
/getlink | POST /api/channels/:channelId/commands/getlink | None. Returns { agent, url, message }. |
/getwallet AGENT | POST /api/channels/:channelId/commands/getwallet | Optional agentName or name. If omitted, uses the loaded agent. Returns { agent, publicKey, message }. |
/help | POST /api/channels/:channelId/commands/help | None |
/status | POST /api/channels/:channelId/commands/status | None |
/yolo-mode | POST /api/channels/:channelId/commands/yolo-mode | None |
/yolo-mode-off | POST /api/channels/:channelId/commands/yolo-mode-off | None |
/mute | POST /api/channels/:channelId/commands/mute | None |
/list | POST /api/channels/:channelId/commands/list | None |
/balance | POST /api/channels/:channelId/commands/balance | None |
/fund | POST /api/channels/:channelId/commands/fund | None |
/login | POST /api/channels/:channelId/commands/login | None |
/logout | POST /api/channels/:channelId/commands/logout | None |
/deload | POST /api/channels/:channelId/commands/deload | None |
/repos | POST /api/channels/:channelId/commands/repos | None |
/create NAME | POST /api/channels/:channelId/commands/create | Required name or agentName |
/spawn REPO | POST /api/channels/:channelId/commands/spawn | Required repo, repoName, or fullName |
/load NAME | POST /api/channels/:channelId/commands/load | Required name or agentName |
/share NAME | POST /api/channels/:channelId/commands/share | Required name or agentName; use with group fields |
/share-agent NAME | POST /api/channels/:channelId/commands/share-agent | Required name or agentName; use with group fields |
/create-shared NAME | POST /api/channels/:channelId/commands/create-shared | Required name or agentName; use with group fields |
/unshare-agent NAME | POST /api/channels/:channelId/commands/unshare-agent | Required name or agentName; use with group fields |
/start [NAME] | POST /api/channels/:channelId/commands/start | Optional name or agentName |
/start_agent [NAME] | POST /api/channels/:channelId/commands/start-agent | Optional name or agentName |
/stop [NAME] | POST /api/channels/:channelId/commands/stop | Optional name or agentName |
/stop_agent [NAME] | POST /api/channels/:channelId/commands/stop-agent | Optional name or agentName |
/restart [NAME] | POST /api/channels/:channelId/commands/restart | Optional name or agentName |
/delete NAME | POST /api/channels/:channelId/commands/delete | Required name or agentName |
/fund_agent AGENT AMOUNT | POST /api/channels/:channelId/commands/fund-agent | Required agentName or name; required amountUsd or amount |
/fund_agent AGENT AMOUNT | POST /api/channels/:channelId/commands/fund_agent | Alias for /commands/fund-agent |
/fork NAME REPO | POST /api/channels/:channelId/commands/fork | Required name or agentName; required repo, repoUrl, or fullName |
/yolo META-COMMAND | POST /api/channels/:channelId/commands/yolo | Required prompt, text, or command |
/prompt MESSAGE | POST /api/channels/:channelId/commands/prompt | Required prompt, text, or task; legacy loaded-agent path |
Command endpoint argument reference
| Endpoint | Request body arguments | Behavior |
|---|---|---|
POST /api/channels/:channelId/commands/help | Common fields only. | Returns the available command list for the user or group. |
POST /api/channels/:channelId/commands/status | Common fields only. | Returns the currently loaded agent and status. |
POST /api/channels/:channelId/commands/yolo-mode | Common fields only. | Enables YOLO mode for this user in direct conversations. |
POST /api/channels/:channelId/commands/yolo-mode-off | Common fields only. | Disables YOLO mode for this user. |
POST /api/channels/:channelId/commands/mute | Common fields only. | Mutes currently active task notifications until completion. |
POST /api/channels/:channelId/commands/list | Common fields only. Include isGroup and groupExternalId to list shared agents in a group. | Lists the user's agents in DMs or the group's shared agents in groups. |
POST /api/channels/:channelId/commands/balance | Common fields only. | Returns account balance, free credits/hours, and deposit address when available. |
POST /api/channels/:channelId/commands/fund | Common fields only. | Returns funding instructions. |
POST /api/channels/:channelId/commands/login | Common fields only. | Returns a one-click sign-in link for the channel user. |
POST /api/channels/:channelId/commands/logout | Common fields only. | Logs the channel user out of message-based access and unloads their agent. |
POST /api/channels/:channelId/commands/deload | Common fields only. | Unloads the current direct-conversation agent for the user. |
POST /api/channels/:channelId/commands/repos | Common fields only. | Lists repositories connected to the user's chat.dev account. |
POST /api/channels/:channelId/commands/getlink | Common fields only. Include groupExternalId to use the group's loaded agent. | Returns { agent, url, message }; url opens the live agent page after signing the user in. |
POST /api/channels/:channelId/commands/getwallet | Optional agentName or name. If omitted, uses the loaded agent. Include groupExternalId to search group-shared agents. | Returns { agent, publicKey, message }. |
POST /api/channels/:channelId/commands/create | Required name or agentName. | Creates a new direct-conversation agent and loads it for the user. |
POST /api/channels/:channelId/commands/spawn | Required repo, repoName, or fullName. | Creates and loads an agent from a connected repository. |
POST /api/channels/:channelId/commands/load | Required name or agentName. Include group fields to load a shared agent in a group. | Loads an existing agent for future messages. |
POST /api/channels/:channelId/commands/share | Required name or agentName; also include isGroup: true and groupExternalId. | Shares an owned agent into a group so other group members can talk to it. Owners can talk to their own agents in group chats without sharing first. |
POST /api/channels/:channelId/commands/share-agent | Required name or agentName; also include isGroup: true and groupExternalId. | Alias behavior for /share-agent NAME. |
POST /api/channels/:channelId/commands/create-shared | Required name or agentName; also include isGroup: true and groupExternalId. | Creates a new agent, shares it into the group, and loads it there. |
POST /api/channels/:channelId/commands/unshare-agent | Required name or agentName; also include isGroup: true and groupExternalId. | Removes an owned shared agent from the group. |
POST /api/channels/:channelId/commands/start | Optional name or agentName. If omitted, uses the loaded agent. | Starts the named or loaded agent. |
POST /api/channels/:channelId/commands/start-agent | Optional name or agentName. If omitted, uses the loaded agent. | Alias behavior for /start_agent. |
POST /api/channels/:channelId/commands/stop | Optional name or agentName. If omitted, uses the loaded agent. | Stops the named or loaded agent. |
POST /api/channels/:channelId/commands/stop-agent | Optional name or agentName. If omitted, uses the loaded agent. | Alias behavior for /stop_agent. |
POST /api/channels/:channelId/commands/restart | Optional name or agentName. If omitted, uses the loaded agent. | Restarts the named or loaded agent. |
POST /api/channels/:channelId/commands/delete | Required name or agentName. | Deletes the named agent if the channel user has permission. |
POST /api/channels/:channelId/commands/fund-agent | Required agentName or name; required amountUsd or amount. | Returns instructions to fund the agent wallet with the requested USD amount. |
POST /api/channels/:channelId/commands/fund_agent | Required agentName or name; required amountUsd or amount. | Alias for /commands/fund-agent. |
POST /api/channels/:channelId/commands/fork | Required name or agentName; required repo, repoUrl, or fullName. | Creates a new agent attached to a connected repository. |
POST /api/channels/:channelId/commands/yolo | Required prompt, text, or command. | Runs the meta-command through chat.dev's AI router. |
POST /api/channels/:channelId/commands/prompt | Required prompt, text, or task. | Sends a task to the currently loaded agent. In group chats, prefer @agentname message or @chatdev message; /prompt remains for legacy loaded-agent integrations. |
Command request examples
Create an agent:
{
"externalUserId": "U12345",
"displayName": "Alice",
"name": "my-agent"
}
Load or start an agent:
{
"externalUserId": "U12345",
"agentName": "my-agent"
}
Send a task to the loaded agent:
{
"externalUserId": "U12345",
"prompt": "Fix the failing checkout test and open a PR."
}
Run a group command:
{
"externalUserId": "U12345",
"displayName": "Alice",
"isGroup": true,
"groupExternalId": "G67890",
"groupName": "Engineering",
"agentName": "team-agent"
}
Fund an agent:
{
"externalUserId": "U12345",
"agentName": "my-agent",
"amountUsd": 25
}
Fork an agent from a connected repo:
{
"externalUserId": "U12345",
"name": "bugfix-agent",
"repo": "owner/repo"
}
Get loaded agent terminal link
POST /api/channels/:channelId/commands/getlink
Authorization: Bearer <apiKey>
Content-Type: application/json
{
"externalUserId": "U12345",
"displayName": "Alice",
"groupExternalId": "G67890"
}
groupExternalId is optional. Include it when the button was clicked in a group conversation so chat.dev can use the group's loaded shared agent.
Response:
{
"agent": { "name": "my-agent", "status": "running" },
"url": "https://chat.dev/api/auth/session-login?sid=...",
"message": "Open \"my-agent\" in chat.dev: https://chat.dev/api/auth/session-login?sid=..."
}
Opening url signs the user into chat.dev and shows the real agent page with the live terminal. This is intentionally different from the public sanitized history page.
Get agent wallet public key
POST /api/channels/:channelId/commands/getwallet
Authorization: Bearer <apiKey>
Content-Type: application/json
{
"externalUserId": "U12345",
"displayName": "Alice",
"agentName": "my-agent",
"groupExternalId": "G67890"
}
agentName is optional. If omitted, chat.dev uses the loaded agent for the user or group. groupExternalId is optional; include it to look up an agent shared into that group before falling back to the user's own agents.
Response:
{
"agent": { "name": "my-agent", "status": "running" },
"publicKey": "7xK...",
"message": "my-agent wallet public key: 7xK..."
}
Text command shortcuts#
Channel UIs can also expose text shortcuts. When your app sends:
{
"externalUserId": "U12345",
"message": "/getlink"
}
chat.dev replies with a one-click login URL for the currently loaded agent.
When your app sends:
{
"externalUserId": "U12345",
"message": "/getwallet my-agent"
}
chat.dev replies with that agent's wallet public key.
Outbound webhook format#
When an agent sends a response, chat.dev POSTs to your callback URL:
{
"event": "message.send",
"channelId": "ch_abc123",
"externalUserId": "U12345",
"groupExternalId": "G67890",
"message": "I've created the Express server. Here's what I did..."
}
Headers:
Content-Type: application/jsonX-Webhook-Signature: <HMAC-SHA256 hex digest>X-Channel-Id: ch_abc123
Delivery expectations:
- Respond with any 2xx status after you accept the message.
- Use
externalUserIdto route DMs. - Use
groupExternalIdwhen present to route group messages. - Validate the signature against the raw request body using the channel's
webhookSecret. - Treat duplicate payloads idempotently; future retry behavior may redeliver the same logical message.
Text-only channel registration#
For integrations that operate purely via messaging (no browser onboarding), register a text-only channel using a registration key. This bypasses the session-cookie requirement entirely.
POST /api/channels/register
Content-Type: application/json
{
"registrationKey": "<secret>",
"name": "My Telegram Bot",
"callbackUrl": "https://your-server.com/chat-dev-webhook",
"platform": "telegram",
"ownerEmail": "you@example.com"
}
| Field | Required | Description |
|---|---|---|
registrationKey | Yes | Secret registration key (contact us to obtain) |
name | Yes | Channel display name |
callbackUrl | Yes | Your HTTPS webhook endpoint for outbound messages from chat.dev |
platform | No | Platform identifier (default: custom) |
ownerEmail | No | Email for the channel owner account. If omitted, a new account is auto-created. |
Response (201):
{
"id": "ch_abc123",
"name": "My Telegram Bot",
"platform": "telegram",
"callbackUrl": "https://your-server.com/chat-dev-webhook",
"apiKey": "chk_...",
"webhookSecret": "whsec_...",
"textOnly": true
}
How text-only channels differ#
- No browser required to start. Users can create and use agents from the channel immediately.
- Freemium account setup. Channel-created users receive the same free agent/runtime/API allowance as lightweight web signups. They only receive a continue-setup link after they hit the free agent or prompt/API usage limits.
- Auto-onboarding. On a user's first message, they receive a welcome message with an explanation of chat.dev, available commands, links to the Terms of Service and Privacy Policy, and their auto-generated SOL wallet address.
- Implicit consent. The welcome message states: "By continuing to communicate with this channel you agree to these terms."
- SOL wallet. Each new user automatically gets a Solana deposit address for funding agents. The address is included in the welcome text and in a structured JSON reply:
{
"type": "welcome",
"solAddress": "7xK...",
"tosUrl": "https://chat.dev/terms",
"privacyUrl": "https://chat.dev/privacy"
}
Structured replies are prefixed with __structured: in the replies array. Parse the JSON after that prefix to extract wallet addresses programmatically.
User accounts#
Channel users get auto-created chat.dev accounts on first message. Each user is identified by channelId + externalUserId. Users can link their channel identity to an existing chat.dev account via the /login command.
All commands#
Channel users have access to the same commands as SMS users. See the Commands page.
Every slash command exposed to channel users can be called as a dedicated API endpoint under POST /api/channels/:channelId/commands/.... The complete endpoint list and request fields are in the Channel command endpoints section above.