Skip to Content
Polyant is open source under AGPL-3.0 — star us on GitHub.
Admin PanelChannels

Tab: Channels

The Channels tab connects an instance to messaging surfaces. Five channels are configurable here: Telegram, Slack, WhatsApp (via Twilio), the synchronous HTTP channel, and the in-process Agent channel. Each has its own card with sensitive fields hidden behind password-style inputs.

For step-by-step setup of each provider (BotFather, Slack app, Twilio sandbox), see Connect a Channel. This page is the field reference.

Telegram

What you need

  1. Open Telegram, message @BotFather, run /newbot. Follow prompts.
  2. Save the bot token that BotFather hands back. It looks like 123456789:ABCdef....

Fields on the Channels tab

  • Bot Token (sensitive) — the BotFather token.
  • Allowed User IDs — a comma-separated list of Telegram numeric user ids. When set, the bot only replies to those users; everyone else is silently ignored. Leave blank to accept everyone.

Starting

Save, then click Start channel. Within seconds the engine logs “Telegram channel started for instance <slug>” and your bot is live. Test by messaging it from your own Telegram account.

Capabilities

  • Text, photos, documents, audio (transcribed by Deepgram if configured, otherwise ignored).
  • The fragmented-burst coordinator (see “The fragmented-burst coordinator” below) coalesces multiple quick messages into one pipeline run.

Slack

What you need

A Slack app with Socket Mode enabled. Required scopes:

  • chat:write
  • app_mentions:read
  • im:history
  • im:write
  • users:read

Socket Mode means the engine connects out to Slack — no public webhook required.

Fields on the Channels tab

  • Bot Token (sensitive) — starts with xoxb-.
  • App Token (sensitive) — starts with xapp-. Required for Socket Mode.
  • Signing Secret (sensitive) — the app’s signing secret. Required by the form schema (older drafts of the docs marked it optional — it is not). The secret is used to verify request signatures on Slack interactivity callbacks and to power DM/mention resolution (the adapter looks up users by id and opens DMs on demand).

Starting

Save, click Start channel. The engine opens a WebSocket to Slack. Test by @-mentioning the bot in any channel it has been invited to, or by DMing it directly.

Capabilities

  • Threaded replies in channels (@-mentions create or continue a thread).
  • DMs.
  • Text-only inbound (the Slack adapter ignores files[] payloads — uploaded images, documents, and voice clips are dropped). Attachments are supported only on Telegram and WhatsApp.
  • Tools like slackPostMessage can target specific channels (#general, channel id, or user id), and are not bound to the inbound channel — a conversation that started on WhatsApp, web, or a webhook trigger can fan out to Slack as long as the instance has Slack configured here. See Tool catalog → Messaging for the full contract, including the optional slack_allowed_channels allowlist.

WhatsApp (via Twilio)

What you need

A Twilio account with the WhatsApp sandbox or a production WhatsApp Business number, plus an inbound webhook configured to point at https://<your-base-url>/webhooks/twilio/<instanceSlug>/whatsapp (served by twilio-webhook.controller.ts).

Fields on the Channels tab

  • Account SID — Twilio account SID.
  • Auth Token (sensitive) — Twilio auth token.
  • WhatsApp Numberwhatsapp:+14155238886 for the sandbox, or your production number.

Starting

Save and Start channel. WhatsApp does not “start” in the same sense as Telegram or Slack — there is no socket. What “start” actually does is register the instance to listen on its webhook route. Stopping the channel deregisters the route.

Capabilities

  • Text, images, audio (Deepgram transcription if configured), documents.
  • “Typing…” indicator (Twilio’s Indicators/Typing.json API): triggered ~1.5 s after the first fragment of a burst, expires after 25 s or on reply.
  • Pre-approved templates via send_whatsapp_template.

HTTP (synchronous REST)

The HTTP channel lets an external system — an orchestrator, a CRM, a contact-centre platform — drive an agent over one REST call, with no adapter to start and no socket to keep open.

The endpoint

POST /channels/http/<slug>/messages Authorization: Bearer <the agent's HTTP API key>

Authentication is the per-agent API key, the same contract as /v1/chat/completions. The route is rate-limited per IP at 300 requests/minute — high on purpose, because one orchestrator drives many conversations from one address; the API key is the real gate.

The body has two reserved fields plus anything else you want to pass:

  • session_id — the caller’s own stable conversation id (for example a Twilio Conversation SID). It becomes the channel id, so the first call creates the conversation and later calls continue it: no server-generated id, no round-trip to learn it. Alphanumeric plus - and _ only, since it becomes part of the conversation id and of the per-conversation workspace path.
  • message — the user text (up to 100,000 characters).
  • conversation_history (optional) — up to 20 prior { role, content } turns to seed into the conversation, so the agent sees, as its own prior turn, something the orchestrator already sent out of band (a reminder, for instance). It works on any call, not just the first, and turns already persisted are skipped — re-sending the same one is a no-op.

Every other top-level field is kept and projected onto the conversation state store through the agent’s field mapping.

Fields on the Channels tab

  • Field mapping — maps conversation-state keys to dot-paths in the caller’s payload (for example the state key phonecustomer.phoneNumber). The mapped values are projected before the turn runs, so a burst’s context is in place whichever fragment triggers it.
  • Outbound — how the reply gets back to the user. Two shapes:
    • sync-response — the call blocks for the agent turn and returns 200 with { sessionId, conversationId, messageId, reply }.
    • An out-of-band transport (today Twilio Conversations, with an optional WhatsApp typing indicator) — the call acks 202 immediately and the reply is delivered in the background. There is no retry; a delivery failure is logged.
  • Label — optional human-facing channel name, surfaced to the prompt.

Which mode to pick

sync-response is the mode to reach for when the caller wants the answer in the same request, and it is the mode where post-model hooks (replaceResponse, regenerate) and blocking governance output gates can still act on the text — nothing has been streamed yet.

The out-of-band modes go through the fragmented-burst coordinator (see below), so several quick messages on the same session collapse into one turn. sync-response bypasses it: debouncing is incompatible with a blocking request/response, so the caller aggregates instead.

Caveat. Because the coordinator cancels and restarts an in-flight run, a fragment arriving mid-turn re-runs the pre-model hooks. That is harmless for a read-only hook and double-fires a non-idempotent write — the same risk class as the WhatsApp/Telegram coordinator.

Opted-out contacts are suppressed on this channel too: an out-of-band reply to an opted-out session is not delivered. See Privacy.

Agent (in-process)

The Agent channel is a virtual, in-process adapter (packages/engine/src/channels/adapters/agent.adapter.ts) that lets one instance invoke another from inside its own tool loop — no network hop, no external credentials.

What it does

When the Agent channel is enabled on an instance, that instance becomes reachable as a callee from every other instance via a synthetic tool named ask_<slug> (e.g. ask_support_bot). The caller passes a free-form question; the callee runs a full supervisor cycle on a fresh conversation scoped to the calling instance, and returns the final assistant message as the tool result.

Fields on the Channels tab

  • Enabled — master switch. There are no credentials: enabling it simply publishes the ask_<slug> synthetic tool to every other instance in the same engine.

Notes

  • Agent-to-agent calls inherit the caller’s auth context.
  • Nesting is not allowed. A callee cannot itself call a third agent: the synthetic tool refuses at depth 1 and returns an error instead of recursing. Each call is also bounded by AGENT_CALL_TIMEOUT_MS (default 60,000 ms); a callee that does not answer in time is aborted and the caller gets a timeout.
  • The call carries callerSlug, callerConversationId, depth, and parentTraceId, so analytics and LangSmith stitch the child turn back onto its parent.
  • Conversations spawned through the Agent channel use the conversation id pattern agent:<callerSlug>:<timestamp> and are visible in the Conversations screen like any other.
  • The agent channel is exempt from opt-out — there is no end-user contact behind it.
  • Agent-to-agent over the network (between separate deployments) is not available.

Stopping channels

Each channel card has a Stop channel button. Stopping unhooks the adapter; the next inbound message is dropped (Telegram, Slack) or returns a 404 (WhatsApp webhook). Conversation state is preserved — restarting the channel resumes where it left off.

Multi-channel behavior

A single instance can have every channel running at once. Each conversation is scoped to a (instanceId, channel, externalUserId) triple, so the same end-user talking to the same bot via Telegram and via Slack has two independent conversations and two independent memories.

The fragmented-burst coordinator

WhatsApp and Telegram users often split a single thought across two or three quick messages:

hey do you have time to look at the spreadsheet?

Without coordination, the engine would launch three separate pipelines. Instead, the inbound message coordinator waits for a 2-second silence (the softDebounceMs default) before firing. Fragments that arrive after the pipeline has started abort the in-flight run and re-arm the timer, up to MESSAGE_MAX_RESTARTS (default 3) consecutive cancels. Beyond that, fragments accumulate and are flushed in a follow-up run.

The user sees one thoughtful answer instead of three scattered ones.

The coordinator is off for Slack, for the OpenAI-compatible API (where messages are already complete units), and for the HTTP channel’s sync-response mode. It is on for the HTTP channel’s out-of-band modes.

Last updated on