Molecule AI

Channels

Connect workspaces to Telegram, Slack, Discord, and Lark/Feishu for social integrations.

Overview

Channels let workspaces send and receive messages on social platforms. Each workspace can have multiple channel integrations — a Telegram bot, a Slack webhook, a Discord webhook, a Lark/Feishu Custom Bot — configured independently with per-channel allowlists and JSONB config.

Outbound messages flow from the workspace through the platform adapter to the social platform. Inbound messages arrive via webhooks (POST /webhooks/:type), are parsed by the adapter, and forwarded to the workspace as A2A message/send requests.

User (Telegram/Slack/Discord/Lark) ──webhook──> Platform ──A2A──> Workspace Agent
                                                     <──adapter──  (response)
User <──bot message────────────────────────────────────────────────/

Adapters

Four adapters are registered out of the box. Use GET /channels/adapters to list them at runtime.

Telegram

Uses the Telegram Bot API. Supports both long-polling (for inbound) and direct API calls (for outbound). The adapter caches BotAPI instances to avoid repeated getMe calls.

Required config fields:

FieldTypeDescription
bot_tokenstringTelegram bot token (123456789:ABCdef...). Validated against a strict regex.
chat_idstringComma-separated chat IDs to listen on and send to.

Features:

  • Long-polling with 30s timeout and 2s retry interval
  • Auto-reply to /start with the chat ID (useful for setup)
  • Bot commands: /start, /help, /reset (clear history), /cancel (best-effort)
  • Long messages automatically split at paragraph/line/word boundaries (4096 char limit)
  • Typing indicator sent while the agent processes
  • Rate-limit handling with retry_after backoff
  • Auto-discovers chats via getUpdates (including my_chat_member events for group adds)
  • Auto-disables the channel when the bot is kicked from a chat

Slack

Uses Slack Incoming Webhooks for outbound and the Slack Events API for inbound.

Required config fields:

FieldTypeDescription
webhook_urlstringSlack Incoming Webhook URL (must start with https://hooks.slack.com/).

Features:

  • Outbound via Incoming Webhook (no OAuth required)
  • Inbound via Events API JSON payload or slash command (URL-encoded form)
  • url_verification challenge handshake supported
  • Slash commands prepend the command name so the agent sees the full invocation

Discord

Uses Discord Incoming Webhooks for outbound and Discord Interactions (slash commands) for inbound. Discord uses a push-based interactions model — there is no long-polling; the platform receives signed payloads at the interactions endpoint.

Required config fields:

FieldTypeDescription
webhook_urlstringDiscord Incoming Webhook URL. Must start with https://discord.com/api/webhooks/. Validated on creation (matches the Slack SSRF-guard pattern).

Global secret:

# Register the webhook URL as a global or per-workspace secret
curl -X PUT http://localhost:8080/settings/secrets \
  -H 'Content-Type: application/json' \
  -d '{"key":"DISCORD_WEBHOOK_URL","value":"https://discord.com/api/webhooks/..."}'

Features:

  • Outbound via Incoming Webhook — POSTs {"content": "<text>"} to the webhook URL
  • Long messages automatically split at newline/space boundaries (Discord 2000-character hard limit)
  • Inbound via Discord Interactions — no long-polling; Discord pushes signed payloads
    • Type 1 PING — router layer responds {"type":1}; adapter returns nil (no A2A forward)
    • Type 2 APPLICATION_COMMAND — slash command, forwarded as /commandname option1 option2
    • Type 3 MESSAGE_COMPONENT — button/select interaction, forwarded as component data
  • User identity prefers member.user (guild) over user (DM) for consistent routing
  • StartPolling is a no-op (returns nil) — Discord uses interactions, not polling

Setup:

  1. Incoming Webhook — Discord Server → channel settings → Integrations → Webhooks → New Webhook → Copy Webhook URL
  2. Add as a secret: PUT /settings/secrets with DISCORD_WEBHOOK_URL
  3. Slash commands (inbound) — create a Discord Application at discord.com/developers, set the Interactions Endpoint URL to https://<platform-host>/webhooks/discord
  4. Verify the endpoint: Discord sends a type-1 PING; the platform responds {"type":1} automatically

Example config:

{
  "type": "discord",
  "config": {
    "webhook_url": "https://discord.com/api/webhooks/1234567890/abcdef..."
  }
}

Discord does not support bot-initiated long-polling. Inbound messages only work via slash commands registered in your Discord Application. If you only need outbound (workspace → Discord), no Application setup is required — just add the webhook URL.


Lark / Feishu

Outbound via Custom Bot webhooks, inbound via Event Subscriptions.

Required config fields:

FieldTypeDescription
webhook_urlstringCustom Bot webhook URL. Must start with https://open.feishu.cn/open-apis/bot/v2/hook/ or https://open.larksuite.com/open-apis/bot/v2/hook/.

Optional config fields:

FieldTypeDescription
verify_tokenstringVerification Token from the app's Event Subscriptions page. When set, inbound events with a mismatching token are rejected.

Features:

  • Both China (open.feishu.cn) and international (open.larksuite.com) endpoints supported
  • url_verification handshake with constant-time verify_token comparison
  • v2 event payload parsing (im.message.receive_v1)
  • Token verification on both url_verification and event_callback payloads
  • Application-level error codes checked (Lark returns HTTP 200 even for app errors)

Setup Flow

1. Create a Channel

curl -X POST http://localhost:8080/workspaces/{id}/channels \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {token}" \
  -d '{
    "type": "telegram",
    "config": {
      "bot_token": "123456789:ABCdefGHIjklmnopQRSTuvwxyz",
      "chat_id": "-1001234567890"
    }
  }'

2. Test the Connection

curl -X POST http://localhost:8080/workspaces/{id}/channels/{channelId}/test \
  -H "Authorization: Bearer {token}"

3. Send a Message

curl -X POST http://localhost:8080/workspaces/{id}/channels/{channelId}/send \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer {token}" \
  -d '{"text": "Hello from the agent!"}'

Inbound Webhooks

Register your platform's public URL as the webhook endpoint for each social platform. Inbound messages arrive at:

POST /webhooks/:type

where :type is telegram, slack, discord, or lark. The platform:

  1. Looks up all channels of that type
  2. Calls the adapter's ParseWebhook to extract a standardized InboundMessage
  3. Checks the allowlist (if configured)
  4. Forwards the message to the workspace via A2A message/send

For Telegram, the platform can also use long-polling instead of webhooks, started automatically when a Telegram channel is created.

For Discord, the platform automatically handles type-1 PING interactions (required by Discord for endpoint verification) and forwards type-2 and type-3 interaction payloads to the workspace.


Discover Chats

Auto-detect available chats for a bot token before creating a channel:

curl -X POST http://localhost:8080/channels/discover \
  -H "Content-Type: application/json" \
  -d '{"type": "telegram", "bot_token": "123456789:ABCdef..."}'

Returns the bot username, discovered chats (with IDs, names, and types), and whether the bot can read all group messages (Telegram privacy mode).


Allowlists

Each channel row has an allowed_users JSONB array. When non-empty, only messages from users whose IDs appear in the list are forwarded to the workspace. All others are silently dropped.


Config Encryption

Sensitive config fields (like bot_token) are encrypted at rest. The List endpoint decrypts them server-side and masks tokens in the response (showing only the first 4 and last 4 characters).


API Reference

MethodPathDescription
GET/channels/adaptersList available adapter types
POST/channels/discoverAuto-detect chats for a bot token
GET/workspaces/:id/channelsList channels for a workspace
POST/workspaces/:id/channelsAdd a channel
PATCH/workspaces/:id/channels/:channelIdUpdate a channel
DELETE/workspaces/:id/channels/:channelIdRemove a channel
POST/workspaces/:id/channels/:channelId/testTest connection
POST/workspaces/:id/channels/:channelId/sendSend outbound message
POST/webhooks/:typeIncoming social webhook

Example Configs

Telegram

{
  "type": "telegram",
  "config": {
    "bot_token": "123456789:ABCdefGHIjklmnopQRSTuvwxyz_1234",
    "chat_id": "-1001234567890"
  }
}

Multiple chats (comma-separated):

{
  "type": "telegram",
  "config": {
    "bot_token": "123456789:ABCdefGHIjklmnopQRSTuvwxyz_1234",
    "chat_id": "-1001234567890, -1009876543210"
  }
}

Slack

{
  "type": "slack",
  "config": {
    "webhook_url": "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
  }
}

Discord

{
  "type": "discord",
  "config": {
    "webhook_url": "https://discord.com/api/webhooks/1234567890123456789/abcdefGHIjklmnopQRSTuvwxyz_1234"
  }
}

Lark / Feishu

{
  "type": "lark",
  "config": {
    "webhook_url": "https://open.larksuite.com/open-apis/bot/v2/hook/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "verify_token": "your-verification-token"
  }
}

China endpoint:

{
  "type": "lark",
  "config": {
    "webhook_url": "https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  }
}

On this page