Channels

One normalized API — connections, bindings, threads, events — with the platform machinery (webhook ingress, socket fleets, rendering, reactions, retries) managed for you. Every conversation runs under your bot identity: your Slack app, your BotFather bot, your Discord bot, your WhatsApp business number, your Azure Bot, your Lark app — and soon your iMessage number.

In this section: Quickstart · Routing · Webhooks · guides for Telegram, Slack, Discord, WhatsApp, Microsoft Teams, Lark, LINE, iMessage (coming soon), and Email (SES). The endpoint-level reference lives in the API reference.

The object model

  • Channel app — your own bot credentials for one platform (POST /v1/channels/apps), encrypted at rest and write-only through the API. Registration proves the credentials against the platform where an API exists and returns the setup steps the platform cannot automate.
  • Connection — one installed conversation surface: a Telegram chat, a Slack channel, a Discord channel, a WhatsApp conversation, a Lark chat or group, an iMessage number pairing.
  • Binding — routes a connection (or one thread of it) to a destination: an AgentSky session, or your own webhook endpoint.
  • Thread ids — plain {platform}:… strings used verbatim for posting and events (e.g. telegram:12345, slack:C0123:1712.0034). Treat them as opaque.
  • Webhook endpoint — where your service receives signed channel events (message.received and friends).

Routing modes

Session destination (zero code) — bind a connection to an AgentSky session: inbound messages become agent turns, replies flow back automatically, and working markers (⏳ → ✅) ride the platform's reactions where supported.

Webhook destination (your code) — bind to a webhook endpoint instead: inbound arrives as signed message.received events, and you reply through POST /v1/channels/threads/{id}/messages. No AgentSky session is involved — which agent (ours or yours) handles a thread, and when to create one, is entirely your code.

Messaging

bash
# post into a thread (markdown renders natively per platform; degrades safely elsewhere)
curl -s -X POST "https://api.agentsky.dev/v1/channels/threads/telegram:12345/messages" \
  -H "Authorization: Bearer $AST_TOKEN" -H 'content-type: application/json' \
  -d '{"parts": [{"type": "markdown", "text": "done — see **the report**"}], "done": true}'

Parts are text, markdown, file ({type: "file", file_id}), or a labeled raw part ({type: "raw", platform, payload}) as the native escape hatch. Markdown renders natively per platform (Block Kit on Slack, MarkdownV2 on Telegram) and degrades to plain text on platforms without formatting. done: true resolves the working marker on the message being answered. Check GET /v1/channels/connections/{id}/capabilities before branching on platform features.

For files, upload through the Files API and pass the returned ID. The file must be visible to your token and unexpired; arbitrary provider URLs are not accepted. Native file replies require capabilities.files (Slack, Discord, Telegram and Lark). Message files total at most 32 MiB; each Discord file is at most 20 MiB. Unsupported channels return an explicit error. Long Markdown replies are sent in complete platform-sized parts.

Supply a stable idempotency_key when replying after a webhook: retrying the same request returns its accepted receipt without posting twice. Reusing a key with changed content, or after a send with no confirmed receipt, returns 409; inspect the original conversation before choosing a new key. Set failed: true to resolve a failed working marker. Do not combine it with done: true.

Where to next

  1. The quickstart gets a BotFather bot answering as your agent in about five minutes.
  2. Routing covers bindings, precedence, re-binding, proactive deliveries, and the hosted connect flow.
  3. Webhooks is the event reference: signatures, retries, idempotency, and the SSE dev mirror.