Channels Overview¶
Channels are adapters that connect agents to messaging platforms. Each channel handles platform authentication, inbound message normalization, outbound replies, and live connection status.
Supported channels¶
| Channel | Status | Config key |
|---|---|---|
| HTTP | ✅ Stable | — (always active) |
| Generic Webhooks | ✅ Stable | agent trigger: webhook |
| Telegram | ✅ Stable | channels.telegram |
| Slack | ✅ Stable | channels.slack |
| Discord | ✅ Stable | channels.discord |
| ✅ Stable | channels.whatsapp |
|
| ✅ Stable | channels.email |
|
| Microsoft Teams | ✅ Stable outbound | channels.teams |
| Google Chat | ✅ Stable outbound | channels.google_chat |
| Outgoing Webhook | ✅ Stable | channels.webhook |
How channels route to agents¶
There are three related configuration layers:
config.yamlchooses which agent a channel adapter routes inbound messages to.- The agent's
SOUL.yamlchannels:list declares which channels that agent is intended to be reachable on. default_output_togives scheduled or one-off sends a destination when the caller does not provideto.
For a simple single-bot channel, agent_id is the routing target:
At runtime, Telegram inbound messages become messages with channel: telegram and agent_id: assistant. The engine handles the message and the channel registry sends the reply back through the same adapter ID.
Inbound replies and outbound reports are intentionally different:
| Need | Configure | Result |
|---|---|---|
| One default bot for cron/non-interactive reports | Top-level channel token plus default_output_to; optionally outbound_only: true |
Schedules and channel.send can send when no per-run destination is supplied |
| One interactive bot for one agent | A bot mapping with agent_id and platform credentials |
Messages to that bot invoke exactly that agent |
| Multiple interactive bots | Multiple bot mappings, one per agent | Each mapping gets its own adapter ID such as telegram-weather-agent |
| One agent to answer in the same chat that triggered it | Agent trigger: channel and matching channels: entry |
Soulacy replies to the inbound chat/thread automatically; no channel.send step is required |
The default outbound sender is a fallback for delivery. It is not the same thing as an interactive bot mapping. Interactive mappings are what make a channel message invoke an agent.
Activation safety¶
Message channels use explicit activation guardrails by default. Enabling a channel should not make every platform message an agent prompt.
Common safety fields:
channels:
telegram:
trigger_phrase: "!soulacy"
ignore_groups: true
allowed_chat_ids: ""
allowed_user_ids: ""
trigger_phrasegates activation. A message must start with this phrase, and the phrase is stripped before the agent sees the text.ignore_groupsdrops group/server/channel messages unless you explicitly set it tofalse.allowed_chat_idsrestricts activation to specific platform destinations.allowed_user_idsrestricts activation to specific senders.
The same model applies to Telegram, Slack, Discord, WhatsApp Cloud API, and WhatsApp Web. HTTP is always active because callers explicitly invoke its API.
Multi-bot mappings¶
Telegram, Slack, and Discord support multiple bot credentials under the same channel type. This is useful when you want separate platform bots mapped to separate agents.
channels:
telegram:
enabled: true
bots:
- bot_name: "Assistant Bot"
token: "BOT_TOKEN_FOR_ASSISTANT"
agent_id: assistant
allowed_user_ids: [123456789]
- bot_name: "Finance Bot"
token: "BOT_TOKEN_FOR_FINANCE"
agent_id: financial-agent
allowed_user_ids: [123456789]
The first bot keeps the canonical adapter ID. Additional bots get deterministic adapter IDs:
| Adapter ID | Agent |
|---|---|
telegram |
assistant |
telegram-financial-agent |
financial-agent |
The same pattern applies to Slack and Discord:
- first bot:
slackordiscord - later bots:
slack-<agent_id>ordiscord-<agent_id>
Managing mappings in the GUI¶
Open Channels in the web UI:
- Guided setup cards walk you through Telegram, Slack, Discord, WhatsApp Cloud, WhatsApp Web, email (SMTP), Microsoft Teams, Google Chat, generic webhooks, and HTTP with step-by-step instructions, per-field hints keyed off the exact adapter config keys, and a Test-delivery button at the bottom.
- Channel cards show Agent mappings, including adapter ID, agent ID, and connection state.
- Channel cards show Delivery checks with failures, warnings, and remedies for missing tokens, missing default destinations, offline adapters, and broken bot mappings.
- Channel cards expose Test and Diagnose paths for outbound delivery.
Failed tests return the same structured diagnosis used in Activity and support
bundles, so
chat not found,missing scope, bad webhook URLs, and rate limits are visible before a cron job depends on them. Email adds SMTP-specific categories — authentication failed, STARTTLS required, relay denied / SPF, recipient rejected, quota, message rejected, TLS handshake — so raw535 5.7.8/550 5.1.1codes don't bubble up as "unknown". - Click Edit on Telegram, Slack, or Discord to manage Bot mappings.
- Bot mapping rows record a friendly bot name and provide an agent ID dropdown populated from your installed agents.
- After saving channel settings, click Restart Gateway from the banner to reconnect adapters.
The same readiness checks are exposed through Providers -> Doctor / GET /api/v1/doctor so production setups can verify channel delivery before relying on scheduled agents.
Scheduled output through a bot¶
Cron agents run internally by default. To publish a scheduled run result to a specific bot, set schedule.output in the agent:
trigger: cron
schedule:
cron: "0 8 * * *"
output:
channel: telegram-financial-agent
to: "123456789"
bot_name: "Finance Bot"
channel is the adapter ID shown in Channels -> Agent mappings. to is the platform destination ID: Telegram chat ID, Slack channel/user ID, Discord channel ID, WhatsApp recipient number, email address, or an override webhook URL for outbound webhook-style channels such as Teams, Google Chat, and generic outgoing webhooks.
If a scheduled run succeeds but no message appears in the destination, check the channel card's Delivery checks first. The most common causes are:
- The adapter ID in
schedule.output.channeldoes not match a registered mapping. - The adapter is offline because the gateway was not restarted after saving credentials.
- The bot token is missing or invalid.
schedule.output.toor the channel-level default output destination is missing.- The bot is not allowed to post to the target group/channel.
- A webhook-style channel URL was rotated or deleted in the destination app.
Generic outbound send¶
Agents and Studio workflows can deliver to any registered adapter with the
built-in channel.send tool:
Tool input:
{
"channel": "telegram",
"to": "123456789",
"text": "Daily report is ready.",
"metadata": {
"source": "daily-report"
}
}
channel is the adapter ID shown in Channels. to is the platform-native
destination: Telegram chat ID, Slack channel/user ID, Discord channel ID,
WhatsApp recipient, email recipient, custom sidecar thread ID, or an override
HTTP URL for the outgoing webhook, Teams, or Google Chat adapters.
If to is omitted, Soulacy can only infer it when the run came from an inbound
chat/thread or the selected channel mapping has default_output_to configured.
For interactive channel agents, prefer returning the final answer normally. Use
channel.send only for out-of-band delivery, such as sending a second copy of a
report to a team channel.
Multi-channel agents¶
An agent can be available on multiple channels simultaneously:
id: assistant
name: Assistant
trigger: channel
llm:
provider: openai
model: gpt-4o-mini
system_prompt: You are a helpful assistant.
channels:
- http
- telegram
- slack
- discord
The channel must also be configured in config.yaml; the agent-side list alone does not create platform credentials.
Channel-specific formatting¶
Each channel adapter handles platform-specific formatting automatically:
- Telegram — Markdown → MarkdownV2 escaping, inline buttons
- Slack — Markdown → Block Kit, thread replies
- Discord — Markdown → Discord markdown, embed cards
- WhatsApp — Plain text (WhatsApp does not support rich formatting)
- HTTP — Raw text or JSON, caller decides rendering