Channel routing
A binding routes a connection — or one thread of it — to a destination: an AgentSky session, or your webhook endpoint. Which agent handles a conversation is a binding you control; with webhook destinations it's entirely your code.
Bindings
# route the whole connection to a session (or {"webhook": "whk_..."})
curl -s -X PUT https://api.agentsky.dev/v1/channels/connections/{id}/binding \
-H "Authorization: Bearer $AST_TOKEN" -H 'content-type: application/json' \
-d '{"destination": {"session": "sess-..."}}'
# route one thread to your webhook endpoint instead
curl -s -X POST https://api.agentsky.dev/v1/channels/bindings \
-H "Authorization: Bearer $AST_TOKEN" -H 'content-type: application/json' \
-d '{"connection_id": "...", "thread_id": "telegram:app_...:12345", "destination": {"webhook": "whk_..."}}'A destination names exactly one of session or webhook. Connection-level routing is a single slot: PUT .../binding replaces whatever the connection routed to before, so there is never a second competing default. POST /bindings creates thread-scoped rules only, and thread-scoped bindings win over the connection default. route_key is a free label for organizing multiple binding rows. An inbound message on a connected surface with no active binding emits channel.unmapped_conversation instead of dropping.
Session mode
Session-bound inbound becomes agent turns automatically: the platform shows a working marker while the agent runs, the reply posts back into the thread as rendered markdown, and turn.refused reaches your endpoints when a turn is rejected.
Re-point a surface with one call — the connection keeps a single active session:
curl -s -X PUT https://api.agentsky.dev/v1/channels/connections/{id}/binding \
-H "Authorization: Bearer $AST_TOKEN" -H 'content-type: application/json' \
-d '{"destination": {"session": "sess-other"}}'
# posts "You're now talking to ..." into the surface and emits channel.reboundWebhook mode
Bind to a webhook endpoint and the service delivers message.received for every inbound; you reply through POST /v1/channels/threads/{thread_id}/messages. Mapping threads to agents — yours or AgentSky sessions you drive via the sessions API — is your code: look the thread up, create what's missing, send the turn.
Proactive deliveries
# fan out to every thread bound to a destination
curl -s -X POST https://api.agentsky.dev/v1/channels/deliveries \
-H "Authorization: Bearer $AST_TOKEN" -H 'content-type: application/json' \
-d '{"destination": {"session": "sess-..."}, "parts": [{"type": "text", "text": "nightly report ready"}]}'The response carries per-thread results; failures also emit delivery.failed.
The connect flow
POST /v1/channels/connections on a link-flow platform (telegram, imessage, whatsapp, lark) returns a PENDING connection with a connect ceremony — a deep link, and for code flows a phone number + LINK: code. Links expire after 15 minutes.
- Hosted connect page —
https://connect.agentsky.dev/connect/{token}renders the ceremony for you; agents can hand the link to an end user mid-conversation (no credentials transit the page). callback_url— pass it on the connect call and the hosted page pollsGET /connect/{token}/status, then redirects the end user to your URL withconnection_id,status, and yourmetadataechoed as query params once the claim lands.- Claim — the end user taps Start (Telegram) or sends the
LINK:code (iMessage, WhatsApp, Lark). The connection flipsCONNECTED, the pre-bounddestinationbecomes the default binding, andchannel.connectedfires with yourmetadata.
Slack and Discord with destination.webhook use the same pending LINK flow; pass your registered app, open the returned hosted URL and send its code in the intended bot conversation. Their session/channel-creation flows still return CONNECTED synchronously and do not accept callback_url.
The hosted page shows pending, connected, expired or disconnected status even without a callback, retries after temporary network failures, and preserves the result when refreshed. Your authenticated app can poll GET /v1/channels/connections/status?token={token}; it returns the effective connection_id, platform, metadata and lowercase status. The token is the part after LINK: or the Telegram start parameter. Treat it as private. Reconnecting a conversation can replace the pending ID with its existing ID. A connected status proves the claim, not that your webhook has finished processing it; honor normal API rate limits.
Migrate an existing verified conversation
An admin-scoped token can call POST /v1/channels/connections with platform, an owned active app, one destination and adopt: {thread_id, author_id}. Use this only when your application already verified the sender binding and consent. AgentSky checks the app-scoped thread and destination; adoption does not challenge the user or send a platform message. Preserve the original identity and conversation metadata.
An identical replay reuses the connection. A different sender, metadata or destination, an inaccessible workspace, or a disconnected record is rejected. Do not combine adoption with callback_url or invite_user. Ordinary new connections should use the LINK ceremony.
AgentSky