diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9efbb243d..c3fa749cd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## 2025.1.12 (Unreleased)
### Highlights
+- **BREAKING:** rename chat “providers” (Slack/Telegram/WhatsApp/…) to **channels** across CLI/RPC/config; legacy config keys auto-migrate on load (and are written back as `channels.*`).
- Memory: add vector search for agent memories (Markdown-only) with SQLite index, chunking, lazy sync + file watch, and per-agent enablement/fallback.
- Plugins: restore full voice-call plugin parity (Telnyx/Twilio, streaming, inbound policies, tools/CLI).
- Models: add Synthetic provider plus Moonshot Kimi K2 0905 + turbo/thinking variants (with docs).
diff --git a/README.md b/README.md
index 515c12af1..11d42c690 100644
--- a/README.md
+++ b/README.md
@@ -16,13 +16,13 @@
**Clawdbot** is a *personal AI assistant* you run on your own devices.
-It answers you on the providers you already use (WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Microsoft Teams, WebChat), can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant.
+It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Microsoft Teams, WebChat), can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant.
If you want a personal, single-user assistant that feels local, fast, and always-on, this is it.
[Website](https://clawdbot.com) · [Docs](https://docs.clawd.bot) · [Getting Started](https://docs.clawd.bot/start/getting-started) · [Updating](https://docs.clawd.bot/install/updating) · [Showcase](https://docs.clawd.bot/start/showcase) · [FAQ](https://docs.clawd.bot/start/faq) · [Wizard](https://docs.clawd.bot/start/wizard) · [Nix](https://github.com/clawdbot/nix-clawdbot) · [Docker](https://docs.clawd.bot/install/docker) · [Discord](https://discord.gg/clawd)
-Preferred setup: run the onboarding wizard (`clawdbot onboard`). It walks through gateway, workspace, providers, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**.
+Preferred setup: run the onboarding wizard (`clawdbot onboard`). It walks through gateway, workspace, channels, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**.
Works with npm, pnpm, or bun.
New install? Start here: [Getting started](https://docs.clawd.bot/start/getting-started)
@@ -54,7 +54,7 @@ The wizard installs the Gateway daemon (launchd/systemd user service) so it stay
Runtime: **Node ≥22**.
-Full beginner guide (auth, pairing, providers): [Getting started](https://docs.clawd.bot/start/getting-started)
+Full beginner guide (auth, pairing, channels): [Getting started](https://docs.clawd.bot/start/getting-started)
```bash
clawdbot onboard --install-daemon
@@ -97,17 +97,17 @@ Clawdbot connects to real messaging surfaces. Treat inbound DMs as **untrusted i
Full security guide: [Security](https://docs.clawd.bot/gateway/security)
Default behavior on Telegram/WhatsApp/Signal/iMessage/Microsoft Teams/Discord/Slack:
-- **DM pairing** (`dmPolicy="pairing"` / `discord.dm.policy="pairing"` / `slack.dm.policy="pairing"`): unknown senders receive a short pairing code and the bot does not process their message.
-- Approve with: `clawdbot pairing approve ` (then the sender is added to a local allowlist store).
-- Public inbound DMs require an explicit opt-in: set `dmPolicy="open"` and include `"*"` in the provider allowlist (`allowFrom` / `discord.dm.allowFrom` / `slack.dm.allowFrom`).
+- **DM pairing** (`dmPolicy="pairing"` / `channels.discord.dm.policy="pairing"` / `channels.slack.dm.policy="pairing"`): unknown senders receive a short pairing code and the bot does not process their message.
+- Approve with: `clawdbot pairing approve ` (then the sender is added to a local allowlist store).
+- Public inbound DMs require an explicit opt-in: set `dmPolicy="open"` and include `"*"` in the channel allowlist (`allowFrom` / `channels.discord.dm.allowFrom` / `channels.slack.dm.allowFrom`).
Run `clawdbot doctor` to surface risky/misconfigured DM policies.
## Highlights
-- **[Local-first Gateway](https://docs.clawd.bot/gateway)** — single control plane for sessions, providers, tools, and events.
-- **[Multi-provider inbox](https://docs.clawd.bot/providers)** — WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Microsoft Teams, WebChat, macOS, iOS/Android.
-- **[Multi-agent routing](https://docs.clawd.bot/gateway/configuration)** — route inbound providers/accounts/peers to isolated agents (workspaces + per-agent sessions).
+- **[Local-first Gateway](https://docs.clawd.bot/gateway)** — single control plane for sessions, channels, tools, and events.
+- **[Multi-channel inbox](https://docs.clawd.bot/channels)** — WhatsApp, Telegram, Slack, Discord, Signal, iMessage, Microsoft Teams, WebChat, macOS, iOS/Android.
+- **[Multi-agent routing](https://docs.clawd.bot/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions).
- **[Voice Wake](https://docs.clawd.bot/nodes/voicewake) + [Talk Mode](https://docs.clawd.bot/nodes/talk)** — always-on speech for macOS/iOS/Android with ElevenLabs.
- **[Live Canvas](https://docs.clawd.bot/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.clawd.bot/platforms/mac/canvas#canvas-a2ui).
- **[First-class tools](https://docs.clawd.bot/tools)** — browser, canvas, nodes, cron, sessions, and Discord/Slack actions.
@@ -127,9 +127,9 @@ Run `clawdbot doctor` to surface risky/misconfigured DM policies.
- [Session model](https://docs.clawd.bot/concepts/session): `main` for direct chats, group isolation, activation modes, queue modes, reply-back. Group rules: [Groups](https://docs.clawd.bot/concepts/groups).
- [Media pipeline](https://docs.clawd.bot/nodes/images): images/audio/video, transcription hooks, size caps, temp file lifecycle. Audio details: [Audio](https://docs.clawd.bot/nodes/audio).
-### Providers
-- [Providers](https://docs.clawd.bot/providers): [WhatsApp](https://docs.clawd.bot/providers/whatsapp) (Baileys), [Telegram](https://docs.clawd.bot/providers/telegram) (grammY), [Slack](https://docs.clawd.bot/providers/slack) (Bolt), [Discord](https://docs.clawd.bot/providers/discord) (discord.js), [Signal](https://docs.clawd.bot/providers/signal) (signal-cli), [iMessage](https://docs.clawd.bot/providers/imessage) (imsg), [Microsoft Teams](https://docs.clawd.bot/providers/msteams) (Bot Framework), [WebChat](https://docs.clawd.bot/web/webchat).
-- [Group routing](https://docs.clawd.bot/concepts/group-messages): mention gating, reply tags, per-provider chunking and routing. Provider rules: [Providers](https://docs.clawd.bot/providers).
+### Channels
+- [Channels](https://docs.clawd.bot/channels): [WhatsApp](https://docs.clawd.bot/channels/whatsapp) (Baileys), [Telegram](https://docs.clawd.bot/channels/telegram) (grammY), [Slack](https://docs.clawd.bot/channels/slack) (Bolt), [Discord](https://docs.clawd.bot/channels/discord) (discord.js), [Signal](https://docs.clawd.bot/channels/signal) (signal-cli), [iMessage](https://docs.clawd.bot/channels/imessage) (imsg), [Microsoft Teams](https://docs.clawd.bot/channels/msteams) (Bot Framework), [WebChat](https://docs.clawd.bot/web/webchat).
+- [Group routing](https://docs.clawd.bot/concepts/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.clawd.bot/channels).
### Apps + nodes
- [macOS app](https://docs.clawd.bot/platforms/macos): menu bar control plane, [Voice Wake](https://docs.clawd.bot/nodes/voicewake)/PTT, [Talk Mode](https://docs.clawd.bot/nodes/talk) overlay, [WebChat](https://docs.clawd.bot/web/webchat), debug tools, [remote gateway](https://docs.clawd.bot/gateway/remote) control.
@@ -145,10 +145,10 @@ Run `clawdbot doctor` to surface risky/misconfigured DM policies.
- [Skills platform](https://docs.clawd.bot/tools/skills): bundled, managed, and workspace skills with install gating + UI.
### Runtime + safety
-- [Provider routing](https://docs.clawd.bot/concepts/provider-routing), [retry policy](https://docs.clawd.bot/concepts/retry), and [streaming/chunking](https://docs.clawd.bot/concepts/streaming).
+- [Channel routing](https://docs.clawd.bot/concepts/channel-routing), [retry policy](https://docs.clawd.bot/concepts/retry), and [streaming/chunking](https://docs.clawd.bot/concepts/streaming).
- [Presence](https://docs.clawd.bot/concepts/presence), [typing indicators](https://docs.clawd.bot/concepts/typing-indicators), and [usage tracking](https://docs.clawd.bot/concepts/usage-tracking).
- [Models](https://docs.clawd.bot/concepts/models), [model failover](https://docs.clawd.bot/concepts/model-failover), and [session pruning](https://docs.clawd.bot/concepts/session-pruning).
-- [Security](https://docs.clawd.bot/gateway/security) and [troubleshooting](https://docs.clawd.bot/providers/troubleshooting).
+- [Security](https://docs.clawd.bot/gateway/security) and [troubleshooting](https://docs.clawd.bot/channels/troubleshooting).
### Ops + packaging
- [Control UI](https://docs.clawd.bot/web) + [WebChat](https://docs.clawd.bot/web/webchat) served directly from the Gateway.
@@ -315,13 +315,13 @@ Minimal `~/.clawdbot/clawdbot.json` (model + defaults):
Details: [Security guide](https://docs.clawd.bot/gateway/security) · [Docker + sandboxing](https://docs.clawd.bot/install/docker) · [Sandbox config](https://docs.clawd.bot/gateway/configuration)
-### [WhatsApp](https://docs.clawd.bot/providers/whatsapp)
+### [WhatsApp](https://docs.clawd.bot/channels/whatsapp)
- Link the device: `pnpm clawdbot providers login` (stores creds in `~/.clawdbot/credentials`).
- Allowlist who can talk to the assistant via `whatsapp.allowFrom`.
- If `whatsapp.groups` is set, it becomes a group allowlist; include `"*"` to allow all.
-### [Telegram](https://docs.clawd.bot/providers/telegram)
+### [Telegram](https://docs.clawd.bot/channels/telegram)
- Set `TELEGRAM_BOT_TOKEN` or `telegram.botToken` (env wins).
- Optional: set `telegram.groups` (with `telegram.groups."*".requireMention`); when set, it is a group allowlist (include `"*"` to allow all). Also `telegram.allowFrom` or `telegram.webhookUrl` as needed.
@@ -334,11 +334,11 @@ Details: [Security guide](https://docs.clawd.bot/gateway/security) · [Docker +
}
```
-### [Slack](https://docs.clawd.bot/providers/slack)
+### [Slack](https://docs.clawd.bot/channels/slack)
- Set `SLACK_BOT_TOKEN` + `SLACK_APP_TOKEN` (or `slack.botToken` + `slack.appToken`).
-### [Discord](https://docs.clawd.bot/providers/discord)
+### [Discord](https://docs.clawd.bot/channels/discord)
- Set `DISCORD_BOT_TOKEN` or `discord.token` (env wins).
- Optional: set `commands.native`, `commands.text`, or `commands.useAccessGroups`, plus `discord.dm.allowFrom`, `discord.guilds`, or `discord.mediaMaxMb` as needed.
@@ -351,11 +351,11 @@ Details: [Security guide](https://docs.clawd.bot/gateway/security) · [Docker +
}
```
-### [Signal](https://docs.clawd.bot/providers/signal)
+### [Signal](https://docs.clawd.bot/channels/signal)
- Requires `signal-cli` and a `signal` config section.
-### [iMessage](https://docs.clawd.bot/providers/imessage)
+### [iMessage](https://docs.clawd.bot/channels/imessage)
- macOS only; Messages must be signed in.
- If `imessage.groups` is set, it becomes a group allowlist; include `"*"` to allow all.
@@ -395,7 +395,7 @@ Use these when you’re past the onboarding flow and want the deeper reference.
- [Set up Gmail Pub/Sub triggers.](https://docs.clawd.bot/automation/gmail-pubsub)
- [Learn the macOS menu bar companion details.](https://docs.clawd.bot/platforms/mac/menu-bar)
- [Platform guides: Windows (WSL2)](https://docs.clawd.bot/platforms/windows), [Linux](https://docs.clawd.bot/platforms/linux), [macOS](https://docs.clawd.bot/platforms/macos), [iOS](https://docs.clawd.bot/platforms/ios), [Android](https://docs.clawd.bot/platforms/android)
-- [Debug common failures with the troubleshooting guide.](https://docs.clawd.bot/providers/troubleshooting)
+- [Debug common failures with the troubleshooting guide.](https://docs.clawd.bot/channels/troubleshooting)
- [Review security guidance before exposing anything.](https://docs.clawd.bot/gateway/security)
## Advanced docs (discovery + control)
diff --git a/docs/_config.yml b/docs/_config.yml
index 06161b641..13c41bb84 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -41,7 +41,7 @@ nav:
- title: "Android App"
url: "/platforms/android/"
- title: "Telegram"
- url: "/providers/telegram/"
+ url: "/channels/telegram/"
- title: "Security"
url: "/gateway/security/"
- title: "Troubleshooting"
diff --git a/docs/broadcast-groups.md b/docs/broadcast-groups.md
index 07068d0d3..c2f478a02 100644
--- a/docs/broadcast-groups.md
+++ b/docs/broadcast-groups.md
@@ -403,5 +403,5 @@ Planned features:
## See Also
- [Multi-Agent Configuration](/multi-agent-sandbox-tools)
-- [Routing Configuration](/concepts/provider-routing)
+- [Routing Configuration](/concepts/channel-routing)
- [Session Management](/concepts/sessions)
diff --git a/docs/providers/discord.md b/docs/channels/discord.md
similarity index 65%
rename from docs/providers/discord.md
rename to docs/channels/discord.md
index 80b118304..f25f0e623 100644
--- a/docs/providers/discord.md
+++ b/docs/channels/discord.md
@@ -1,7 +1,7 @@
---
summary: "Discord bot support status, capabilities, and configuration"
read_when:
- - Working on Discord provider features
+ - Working on Discord channel features
---
# Discord (Bot API)
@@ -12,7 +12,7 @@ Status: ready for DM and guild text channels via the official Discord bot gatewa
1) Create a Discord bot and copy the bot token.
2) Set the token for Clawdbot:
- Env: `DISCORD_BOT_TOKEN=...`
- - Or config: `discord.token: "..."`.
+ - Or config: `channels.discord.token: "..."`.
3) Invite the bot to your server with message permissions.
4) Start the gateway.
5) DM access is pairing by default; approve the pairing code on first contact.
@@ -20,9 +20,11 @@ Status: ready for DM and guild text channels via the official Discord bot gatewa
Minimal config:
```json5
{
- discord: {
- enabled: true,
- token: "YOUR_BOT_TOKEN"
+ channels: {
+ discord: {
+ enabled: true,
+ token: "YOUR_BOT_TOKEN"
+ }
}
}
```
@@ -30,29 +32,29 @@ Minimal config:
## Goals
- Talk to Clawdbot via Discord DMs or guild channels.
- Direct chats collapse into the agent's main session (default `agent:main:main`); guild channels stay isolated as `agent::discord:channel:` (display names use `discord:#`).
-- Group DMs are ignored by default; enable via `discord.dm.groupEnabled` and optionally restrict by `discord.dm.groupChannels`.
-- Keep routing deterministic: replies always go back to the provider they arrived on.
+- Group DMs are ignored by default; enable via `channels.discord.dm.groupEnabled` and optionally restrict by `channels.discord.dm.groupChannels`.
+- Keep routing deterministic: replies always go back to the channel they arrived on.
## How it works
1. Create a Discord application → Bot, enable the intents you need (DMs + guild messages + message content), and grab the bot token.
2. Invite the bot to your server with the permissions required to read/send messages where you want to use it.
-3. Configure Clawdbot with `DISCORD_BOT_TOKEN` (or `discord.token` in `~/.clawdbot/clawdbot.json`).
-4. Run the gateway; it auto-starts the Discord provider when a token is available (env or config) and `discord.enabled` is not `false`.
+3. Configure Clawdbot with `DISCORD_BOT_TOKEN` (or `channels.discord.token` in `~/.clawdbot/clawdbot.json`).
+4. Run the gateway; it auto-starts the Discord channel when a token is available (env or config) and `channels.discord.enabled` is not `false`.
- If you prefer env vars, set `DISCORD_BOT_TOKEN` (a config block is optional).
5. Direct chats: use `user:` (or a `<@id>` mention) when delivering; all turns land in the shared `main` session. Bare numeric IDs are ambiguous and rejected.
6. Guild channels: use `channel:` for delivery. Mentions are required by default and can be set per guild or per channel.
-7. Direct chats: secure by default via `discord.dm.policy` (default: `"pairing"`). Unknown senders get a pairing code (expires after 1 hour); approve via `clawdbot pairing approve discord `.
- - To keep old “open to anyone” behavior: set `discord.dm.policy="open"` and `discord.dm.allowFrom=["*"]`.
- - To hard-allowlist: set `discord.dm.policy="allowlist"` and list senders in `discord.dm.allowFrom`.
- - To ignore all DMs: set `discord.dm.enabled=false` or `discord.dm.policy="disabled"`.
-8. Group DMs are ignored by default; enable via `discord.dm.groupEnabled` and optionally restrict by `discord.dm.groupChannels`.
-9. Optional guild rules: set `discord.guilds` keyed by guild id (preferred) or slug, with per-channel rules.
-10. Optional native commands: `commands.native` defaults to `"auto"` (on for Discord/Telegram, off for Slack). Override with `discord.commands.native: true|false|"auto"`; `false` clears previously registered commands. Text commands are controlled by `commands.text` and must be sent as standalone `/...` messages. Use `commands.useAccessGroups: false` to bypass access-group checks for commands.
+7. Direct chats: secure by default via `channels.discord.dm.policy` (default: `"pairing"`). Unknown senders get a pairing code (expires after 1 hour); approve via `clawdbot pairing approve discord `.
+ - To keep old “open to anyone” behavior: set `channels.discord.dm.policy="open"` and `channels.discord.dm.allowFrom=["*"]`.
+ - To hard-allowlist: set `channels.discord.dm.policy="allowlist"` and list senders in `channels.discord.dm.allowFrom`.
+ - To ignore all DMs: set `channels.discord.dm.enabled=false` or `channels.discord.dm.policy="disabled"`.
+8. Group DMs are ignored by default; enable via `channels.discord.dm.groupEnabled` and optionally restrict by `channels.discord.dm.groupChannels`.
+9. Optional guild rules: set `channels.discord.guilds` keyed by guild id (preferred) or slug, with per-channel rules.
+10. Optional native commands: `commands.native` defaults to `"auto"` (on for Discord/Telegram, off for Slack). Override with `channels.discord.commands.native: true|false|"auto"`; `false` clears previously registered commands. Text commands are controlled by `commands.text` and must be sent as standalone `/...` messages. Use `commands.useAccessGroups: false` to bypass access-group checks for commands.
- Full command list + config: [Slash commands](/tools/slash-commands)
-11. Optional guild context history: set `discord.historyLimit` (default 20, falls back to `messages.groupChat.historyLimit`) to include the last N guild messages as context when replying to a mention. Set `0` to disable.
-12. Reactions: the agent can trigger reactions via the `discord` tool (gated by `discord.actions.*`).
+11. Optional guild context history: set `channels.discord.historyLimit` (default 20, falls back to `messages.groupChat.historyLimit`) to include the last N guild messages as context when replying to a mention. Set `0` to disable.
+12. Reactions: the agent can trigger reactions via the `discord` tool (gated by `channels.discord.actions.*`).
- Reaction removal semantics: see [/tools/reactions](/tools/reactions).
- - The `discord` tool is only exposed when the current provider is Discord.
+ - The `discord` tool is only exposed when the current channel is Discord.
13. Native commands use isolated session keys (`agent::discord:slash:`) rather than the shared `main` session.
Note: Discord does not provide a simple username → id lookup without extra guild context, so prefer ids or `<@id>` mentions for DM delivery targets.
@@ -117,37 +119,41 @@ Or via config:
```json5
{
- discord: {
- enabled: true,
- token: "YOUR_BOT_TOKEN"
+ channels: {
+ discord: {
+ enabled: true,
+ token: "YOUR_BOT_TOKEN"
+ }
}
}
```
-Multi-account support: use `discord.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
+Multi-account support: use `channels.discord.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
#### Allowlist + channel routing
Example “single server, only allow me, only allow #help”:
```json5
{
- discord: {
- enabled: true,
- dm: { enabled: false },
- guilds: {
- "YOUR_GUILD_ID": {
- users: ["YOUR_USER_ID"],
- requireMention: true,
- channels: {
- help: { allow: true, requireMention: true }
+ channels: {
+ discord: {
+ enabled: true,
+ dm: { enabled: false },
+ guilds: {
+ "YOUR_GUILD_ID": {
+ users: ["YOUR_USER_ID"],
+ requireMention: true,
+ channels: {
+ help: { allow: true, requireMention: true }
+ }
}
+ },
+ retry: {
+ attempts: 3,
+ minDelayMs: 500,
+ maxDelayMs: 30000,
+ jitter: 0.1
}
- },
- retry: {
- attempts: 3,
- minDelayMs: 500,
- maxDelayMs: 30000,
- jitter: 0.1
}
}
}
@@ -158,8 +164,8 @@ Notes:
- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) also count as mentions for guild messages.
- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.
- If `channels` is present, any channel not listed is denied by default.
-- Bot-authored messages are ignored by default; set `discord.allowBots=true` to allow them (own messages remain filtered).
-- Warning: If you allow replies to other bots (`discord.allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `discord.guilds.*.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`.
+- Bot-authored messages are ignored by default; set `channels.discord.allowBots=true` to allow them (own messages remain filtered).
+- Warning: If you allow replies to other bots (`channels.discord.allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `channels.discord.guilds.*.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`.
### 6) Verify it works
1. Start the gateway.
@@ -167,7 +173,7 @@ Notes:
3. If nothing happens: check **Troubleshooting** below.
### Troubleshooting
-- First: run `clawdbot doctor` and `clawdbot providers status --probe` (actionable warnings + quick audits).
+- First: run `clawdbot doctor` and `clawdbot channels status --probe` (actionable warnings + quick audits).
- **“Used disallowed intents”**: enable **Message Content Intent** (and likely **Server Members Intent**) in the Developer Portal, then restart the gateway.
- **Bot connects but never replies in a guild channel**:
- Missing **Message Content Intent**, or
@@ -175,78 +181,80 @@ Notes:
- Your config requires mentions and you didn’t mention it, or
- Your guild/channel allowlist denies the channel/user.
- **`requireMention: false` but still no replies**:
- - `discord.groupPolicy` defaults to **allowlist**; you must either set it to `"open"` or explicitly list the channel under `discord.guilds..channels`.
- - `requireMention` must live under `discord.guilds` (or a specific channel). `discord.requireMention` at the top level is ignored.
-- **Permission audits** (`providers status --probe`) only check numeric channel IDs. If you use slugs/names as `discord.guilds.*.channels` keys, the audit can’t verify permissions.
-- **DMs don’t work**: `discord.dm.enabled=false`, `discord.dm.policy="disabled"`, or you haven’t been approved yet (`discord.dm.policy="pairing"`).
+ - `channels.discord.groupPolicy` defaults to **allowlist**; set it to `"open"` or explicitly list channels under `channels.discord.guilds..channels`.
+ - `requireMention` must live under `channels.discord.guilds` (or a specific channel). `channels.discord.requireMention` at the top level is ignored.
+- **Permission audits** (`channels status --probe`) only check numeric channel IDs. If you use slugs/names as `channels.discord.guilds.*.channels` keys, the audit can’t verify permissions.
+- **DMs don’t work**: `channels.discord.dm.enabled=false`, `channels.discord.dm.policy="disabled"`, or you haven’t been approved yet (`channels.discord.dm.policy="pairing"`).
## Capabilities & limits
- DMs and guild text channels (threads are treated as separate channels; voice not supported).
-- Typing indicators sent best-effort; message chunking uses `discord.textChunkLimit` (default 2000) and splits tall replies by line count (`discord.maxLinesPerMessage`, default 17).
-- File uploads supported up to the configured `discord.mediaMaxMb` (default 8 MB).
+- Typing indicators sent best-effort; message chunking uses `channels.discord.textChunkLimit` (default 2000) and splits tall replies by line count (`channels.discord.maxLinesPerMessage`, default 17).
+- File uploads supported up to the configured `channels.discord.mediaMaxMb` (default 8 MB).
- Mention-gated guild replies by default to avoid noisy bots.
- Reply context is injected when a message references another message (quoted content + ids).
-- Native reply threading is **off by default**; enable with `discord.replyToMode` and reply tags.
+- Native reply threading is **off by default**; enable with `channels.discord.replyToMode` and reply tags.
## Retry policy
-Outbound Discord API calls retry on rate limits (429) using Discord `retry_after` when available, with exponential backoff and jitter. Configure via `discord.retry`. See [Retry policy](/concepts/retry).
+Outbound Discord API calls retry on rate limits (429) using Discord `retry_after` when available, with exponential backoff and jitter. Configure via `channels.discord.retry`. See [Retry policy](/concepts/retry).
## Config
```json5
{
- discord: {
- enabled: true,
- token: "abc.123",
- groupPolicy: "allowlist",
- guilds: {
- "*": {
- channels: {
- general: { allow: true }
- }
- }
- },
- mediaMaxMb: 8,
- actions: {
- reactions: true,
- stickers: true,
- polls: true,
- permissions: true,
- messages: true,
- threads: true,
- pins: true,
- search: true,
- memberInfo: true,
- roleInfo: true,
- roles: false,
- channelInfo: true,
- voiceStatus: true,
- events: true,
- moderation: false
- },
- replyToMode: "off",
- dm: {
+ channels: {
+ discord: {
enabled: true,
- policy: "pairing", // pairing | allowlist | open | disabled
- allowFrom: ["123456789012345678", "steipete"],
- groupEnabled: false,
- groupChannels: ["clawd-dm"]
- },
- guilds: {
- "*": { requireMention: true },
- "123456789012345678": {
- slug: "friends-of-clawd",
- requireMention: false,
- reactionNotifications: "own",
- users: ["987654321098765432", "steipete"],
- channels: {
- general: { allow: true },
- help: {
- allow: true,
- requireMention: true,
- users: ["987654321098765432"],
- skills: ["search", "docs"],
- systemPrompt: "Keep answers short."
+ token: "abc.123",
+ groupPolicy: "allowlist",
+ guilds: {
+ "*": {
+ channels: {
+ general: { allow: true }
+ }
+ }
+ },
+ mediaMaxMb: 8,
+ actions: {
+ reactions: true,
+ stickers: true,
+ polls: true,
+ permissions: true,
+ messages: true,
+ threads: true,
+ pins: true,
+ search: true,
+ memberInfo: true,
+ roleInfo: true,
+ roles: false,
+ channelInfo: true,
+ voiceStatus: true,
+ events: true,
+ moderation: false
+ },
+ replyToMode: "off",
+ dm: {
+ enabled: true,
+ policy: "pairing", // pairing | allowlist | open | disabled
+ allowFrom: ["123456789012345678", "steipete"],
+ groupEnabled: false,
+ groupChannels: ["clawd-dm"]
+ },
+ guilds: {
+ "*": { requireMention: true },
+ "123456789012345678": {
+ slug: "friends-of-clawd",
+ requireMention: false,
+ reactionNotifications: "own",
+ users: ["987654321098765432", "steipete"],
+ channels: {
+ general: { allow: true },
+ help: {
+ allow: true,
+ requireMention: true,
+ users: ["987654321098765432"],
+ skills: ["search", "docs"],
+ systemPrompt: "Keep answers short."
+ }
}
}
}
@@ -323,7 +331,7 @@ To request a threaded reply, the model can include one tag in its output:
- `[[reply_to:]]` — reply to a specific message id from context/history.
Current message ids are appended to prompts as `[message_id: …]`; history entries already include ids.
-Behavior is controlled by `discord.replyToMode`:
+Behavior is controlled by `channels.discord.replyToMode`:
- `off`: ignore tags.
- `first`: only the first outbound chunk/attachment is a reply.
- `all`: every outbound chunk/attachment is a reply.
@@ -336,7 +344,7 @@ Allowlist matching notes:
Native command notes:
- The registered commands mirror Clawdbot’s chat commands.
-- Native commands honor the same allowlists as DMs/guild messages (`discord.dm.allowFrom`, `discord.guilds`, per-channel rules).
+- Native commands honor the same allowlists as DMs/guild messages (`channels.discord.dm.allowFrom`, `channels.discord.guilds`, per-channel rules).
## Tool actions
The agent can call `discord` with actions like:
diff --git a/docs/providers/grammy.md b/docs/channels/grammy.md
similarity index 65%
rename from docs/providers/grammy.md
rename to docs/channels/grammy.md
index 9fa3c30fb..ff0c92c7a 100644
--- a/docs/providers/grammy.md
+++ b/docs/channels/grammy.md
@@ -14,11 +14,11 @@ read_when:
# What we shipped
- **Single client path:** fetch-based implementation removed; grammY is now the sole Telegram client (send + gateway) with the grammY throttler enabled by default.
- **Gateway:** `monitorTelegramProvider` builds a grammY `Bot`, wires mention/allowlist gating, media download via `getFile`/`download`, and delivers replies with `sendMessage/sendPhoto/sendVideo/sendAudio/sendDocument`. Supports long-poll or webhook via `webhookCallback`.
-- **Proxy:** optional `telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`.
-- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `telegram.webhookUrl` is set (otherwise it long-polls).
-- **Sessions:** direct chats collapse into the agent main session (`agent::`); groups use `agent::telegram:group:`; replies route back to the same provider.
-- **Config knobs:** `telegram.botToken`, `telegram.dmPolicy`, `telegram.groups` (allowlist + mention defaults), `telegram.allowFrom`, `telegram.groupAllowFrom`, `telegram.groupPolicy`, `telegram.mediaMaxMb`, `telegram.proxy`, `telegram.webhookSecret`, `telegram.webhookUrl`.
-- **Draft streaming:** optional `telegram.streamMode` uses `sendMessageDraft` in private topic chats (Bot API 9.3+). This is separate from provider block streaming.
+- **Proxy:** optional `channels.telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`.
+- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `channels.telegram.webhookUrl` is set (otherwise it long-polls).
+- **Sessions:** direct chats collapse into the agent main session (`agent::`); groups use `agent::telegram:group:`; replies route back to the same channel.
+- **Config knobs:** `channels.telegram.botToken`, `channels.telegram.dmPolicy`, `channels.telegram.groups` (allowlist + mention defaults), `channels.telegram.allowFrom`, `channels.telegram.groupAllowFrom`, `channels.telegram.groupPolicy`, `channels.telegram.mediaMaxMb`, `channels.telegram.proxy`, `channels.telegram.webhookSecret`, `channels.telegram.webhookUrl`.
+- **Draft streaming:** optional `channels.telegram.streamMode` uses `sendMessageDraft` in private topic chats (Bot API 9.3+). This is separate from channel block streaming.
- **Tests:** grammy mocks cover DM + group mention gating and outbound send; more media/webhook fixtures still welcome.
Open questions
diff --git a/docs/providers/imessage.md b/docs/channels/imessage.md
similarity index 56%
rename from docs/providers/imessage.md
rename to docs/channels/imessage.md
index c68465047..a67c5b744 100644
--- a/docs/providers/imessage.md
+++ b/docs/channels/imessage.md
@@ -13,31 +13,33 @@ Status: external CLI integration. Gateway spawns `imsg rpc` (JSON-RPC over stdio
1) Ensure Messages is signed in on this Mac.
2) Install `imsg`:
- `brew install steipete/tap/imsg`
-3) Configure Clawdbot with `imessage.cliPath` and `imessage.dbPath`.
+3) Configure Clawdbot with `channels.imessage.cliPath` and `channels.imessage.dbPath`.
4) Start the gateway and approve any macOS prompts (Automation + Full Disk Access).
Minimal config:
```json5
{
- imessage: {
- enabled: true,
- cliPath: "/usr/local/bin/imsg",
- dbPath: "/Users//Library/Messages/chat.db"
+ channels: {
+ imessage: {
+ enabled: true,
+ cliPath: "/usr/local/bin/imsg",
+ dbPath: "/Users//Library/Messages/chat.db"
+ }
}
}
```
## What it is
-- iMessage provider backed by `imsg` on macOS.
+- iMessage channel backed by `imsg` on macOS.
- Deterministic routing: replies always go back to iMessage.
- DMs share the agent's main session; groups are isolated (`agent::imessage:group:`).
-- If a multi-participant thread arrives with `is_group=false`, you can still isolate it by `chat_id` using `imessage.groups` (see “Group-ish threads” below).
+- If a multi-participant thread arrives with `is_group=false`, you can still isolate it by `chat_id` using `channels.imessage.groups` (see “Group-ish threads” below).
## Requirements
- macOS with Messages signed in.
- Full Disk Access for Clawdbot + `imsg` (Messages DB access).
- Automation permission when sending.
-- `imessage.cliPath` can point to any command that proxies stdin/stdout (for example, a wrapper script that SSHes to another Mac and runs `imsg rpc`).
+- `channels.imessage.cliPath` can point to any command that proxies stdin/stdout (for example, a wrapper script that SSHes to another Mac and runs `imsg rpc`).
## Setup (fast path)
1) Ensure Messages is signed in on this Mac.
@@ -54,7 +56,7 @@ If you want the bot to send from a **separate iMessage identity** (and keep your
5) Install `imsg`:
- `brew install steipete/tap/imsg`
6) Set up SSH so `ssh @localhost true` works without a password.
-7) Point `imessage.accounts.bot.cliPath` at an SSH wrapper that runs `imsg` as the bot user.
+7) Point `channels.imessage.accounts.bot.cliPath` at an SSH wrapper that runs `imsg` as the bot user.
First-run note: sending/receiving may require GUI approvals (Automation + Full Disk Access) in the *bot macOS user*. If `imsg rpc` looks stuck or exits, log into that user (Screen Sharing helps), run a one-time `imsg chats --limit 1` / `imsg send ...`, approve prompts, then retry.
@@ -72,24 +74,26 @@ exec /usr/bin/ssh -o BatchMode=yes -o ConnectTimeout=5 -T @local
Example config:
```json5
{
- imessage: {
- enabled: true,
- accounts: {
- bot: {
- name: "Bot",
- enabled: true,
- cliPath: "/path/to/imsg-bot",
- dbPath: "/Users//Library/Messages/chat.db"
+ channels: {
+ imessage: {
+ enabled: true,
+ accounts: {
+ bot: {
+ name: "Bot",
+ enabled: true,
+ cliPath: "/path/to/imsg-bot",
+ dbPath: "/Users//Library/Messages/chat.db"
+ }
}
}
}
}
```
-For single-account setups, use flat options (`imessage.cliPath`, `imessage.dbPath`) instead of the `accounts` map.
+For single-account setups, use flat options (`channels.imessage.cliPath`, `channels.imessage.dbPath`) instead of the `accounts` map.
### Remote/SSH variant (optional)
-If you want iMessage on another Mac, set `imessage.cliPath` to a wrapper that runs `imsg` on the remote macOS host over SSH. Clawdbot only needs stdio.
+If you want iMessage on another Mac, set `channels.imessage.cliPath` to a wrapper that runs `imsg` on the remote macOS host over SSH. Clawdbot only needs stdio.
Example wrapper:
```bash
@@ -97,11 +101,11 @@ Example wrapper:
exec ssh -T mac-mini imsg "$@"
```
-Multi-account support: use `imessage.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. Don’t commit `~/.clawdbot/clawdbot.json` (it often contains tokens).
+Multi-account support: use `channels.imessage.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. Don’t commit `~/.clawdbot/clawdbot.json` (it often contains tokens).
## Access control (DMs + groups)
DMs:
-- Default: `imessage.dmPolicy = "pairing"`.
+- Default: `channels.imessage.dmPolicy = "pairing"`.
- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
- Approve via:
- `clawdbot pairing list imessage`
@@ -109,30 +113,32 @@ DMs:
- Pairing is the default token exchange for iMessage DMs. Details: [Pairing](/start/pairing)
Groups:
-- `imessage.groupPolicy = open | allowlist | disabled`.
-- `imessage.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
+- `channels.imessage.groupPolicy = open | allowlist | disabled`.
+- `channels.imessage.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
- Mention gating uses `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) because iMessage has no native mention metadata.
- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.
## How it works (behavior)
-- `imsg` streams message events; the gateway normalizes them into the shared provider envelope.
+- `imsg` streams message events; the gateway normalizes them into the shared channel envelope.
- Replies always route back to the same chat id or handle.
## Group-ish threads (`is_group=false`)
Some iMessage threads can have multiple participants but still arrive with `is_group=false` depending on how Messages stores the chat identifier.
-If you explicitly configure a `chat_id` under `imessage.groups`, Clawdbot treats that thread as a “group” for:
+If you explicitly configure a `chat_id` under `channels.imessage.groups`, Clawdbot treats that thread as a “group” for:
- session isolation (separate `agent::imessage:group:` session key)
- group allowlisting / mention gating behavior
Example:
```json5
{
- imessage: {
- groupPolicy: "allowlist",
- groupAllowFrom: ["+15555550123"],
- groups: {
- "42": { "requireMention": false }
+ channels: {
+ imessage: {
+ groupPolicy: "allowlist",
+ groupAllowFrom: ["+15555550123"],
+ groups: {
+ "42": { "requireMention": false }
+ }
}
}
}
@@ -140,12 +146,12 @@ Example:
This is useful when you want an isolated personality/model for a specific thread (see [Multi-agent routing](/concepts/multi-agent)). For filesystem isolation, see [Sandboxing](/gateway/sandboxing).
## Media + limits
-- Optional attachment ingestion via `imessage.includeAttachments`.
-- Media cap via `imessage.mediaMaxMb`.
+- Optional attachment ingestion via `channels.imessage.includeAttachments`.
+- Media cap via `channels.imessage.mediaMaxMb`.
## Limits
-- Outbound text is chunked to `imessage.textChunkLimit` (default 4000).
-- Media uploads are capped by `imessage.mediaMaxMb` (default 16).
+- Outbound text is chunked to `channels.imessage.textChunkLimit` (default 4000).
+- Media uploads are capped by `channels.imessage.mediaMaxMb` (default 16).
## Addressing / delivery targets
Prefer `chat_id` for stable routing:
@@ -163,20 +169,20 @@ imsg chats --limit 20
Full configuration: [Configuration](/gateway/configuration)
Provider options:
-- `imessage.enabled`: enable/disable provider startup.
-- `imessage.cliPath`: path to `imsg`.
-- `imessage.dbPath`: Messages DB path.
-- `imessage.service`: `imessage | sms | auto`.
-- `imessage.region`: SMS region.
-- `imessage.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
-- `imessage.allowFrom`: DM allowlist (handles or `chat_id:*`). `open` requires `"*"`.
-- `imessage.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
-- `imessage.groupAllowFrom`: group sender allowlist.
-- `imessage.historyLimit` / `imessage.accounts.*.historyLimit`: max group messages to include as context (0 disables).
-- `imessage.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
-- `imessage.includeAttachments`: ingest attachments into context.
-- `imessage.mediaMaxMb`: inbound/outbound media cap (MB).
-- `imessage.textChunkLimit`: outbound chunk size (chars).
+- `channels.imessage.enabled`: enable/disable channel startup.
+- `channels.imessage.cliPath`: path to `imsg`.
+- `channels.imessage.dbPath`: Messages DB path.
+- `channels.imessage.service`: `imessage | sms | auto`.
+- `channels.imessage.region`: SMS region.
+- `channels.imessage.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
+- `channels.imessage.allowFrom`: DM allowlist (handles or `chat_id:*`). `open` requires `"*"`.
+- `channels.imessage.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
+- `channels.imessage.groupAllowFrom`: group sender allowlist.
+- `channels.imessage.historyLimit` / `channels.imessage.accounts.*.historyLimit`: max group messages to include as context (0 disables).
+- `channels.imessage.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
+- `channels.imessage.includeAttachments`: ingest attachments into context.
+- `channels.imessage.mediaMaxMb`: inbound/outbound media cap (MB).
+- `channels.imessage.textChunkLimit`: outbound chunk size (chars).
Related global options:
- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`).
diff --git a/docs/channels/index.md b/docs/channels/index.md
new file mode 100644
index 000000000..80db578dd
--- /dev/null
+++ b/docs/channels/index.md
@@ -0,0 +1,30 @@
+---
+summary: "Messaging platforms Clawdbot can connect to"
+read_when:
+ - You want to choose a chat channel for Clawdbot
+ - You need a quick overview of supported messaging platforms
+---
+# Chat Channels
+
+Clawdbot can talk to you on any chat app you already use. Each channel connects via the Gateway.
+Text is supported everywhere; media and reactions vary by channel.
+
+## Supported channels
+
+- [WhatsApp](/channels/whatsapp) — Most popular; uses Baileys and requires QR pairing.
+- [Telegram](/channels/telegram) — Bot API via grammY; supports groups.
+- [Discord](/channels/discord) — Discord Bot API + Gateway; supports servers, channels, and DMs.
+- [Slack](/channels/slack) — Bolt SDK; workspace apps.
+- [Signal](/channels/signal) — signal-cli; privacy-focused.
+- [iMessage](/channels/imessage) — macOS only; native integration.
+- [Microsoft Teams](/channels/msteams) — Bot Framework; enterprise support.
+- [WebChat](/web/webchat) — Gateway WebChat UI over WebSocket.
+
+## Notes
+
+- Channels can run simultaneously; configure multiple and Clawdbot will route per chat.
+- Group behavior varies by channel; see [Groups](/concepts/groups).
+- DM pairing and allowlists are enforced for safety; see [Security](/gateway/security).
+- Telegram internals: [grammY notes](/channels/grammy).
+- Troubleshooting: [Channel troubleshooting](/channels/troubleshooting).
+- Model providers are documented separately; see [Model Providers](/providers/models).
diff --git a/docs/providers/location.md b/docs/channels/location.md
similarity index 78%
rename from docs/providers/location.md
rename to docs/channels/location.md
index 7d610e7ff..f38031fb6 100644
--- a/docs/providers/location.md
+++ b/docs/channels/location.md
@@ -1,13 +1,13 @@
---
-summary: "Inbound provider location parsing (Telegram + WhatsApp) and context fields"
+summary: "Inbound channel location parsing (Telegram + WhatsApp) and context fields"
read_when:
- - Adding or modifying provider location parsing
+ - Adding or modifying channel location parsing
- Using location context fields in agent prompts or tools
---
-# Provider location parsing
+# Channel location parsing
-Clawdbot normalizes shared locations from chat providers into:
+Clawdbot normalizes shared locations from chat channels into:
- human-readable text appended to the inbound body, and
- structured fields in the auto-reply context payload.
@@ -25,7 +25,7 @@ Locations are rendered as friendly lines without brackets:
- Live share:
- `🛰 Live location: 48.858844, 2.294351 ±12m`
-If the provider includes a caption/comment, it is appended on the next line:
+If the channel includes a caption/comment, it is appended on the next line:
```
📍 48.858844, 2.294351 ±12m
Meet here
@@ -41,6 +41,6 @@ When a location is present, these fields are added to `ctx`:
- `LocationSource` (`pin | place | live`)
- `LocationIsLive` (boolean)
-## Provider notes
+## Channel notes
- **Telegram**: venues map to `LocationName/LocationAddress`; live locations use `live_period`.
- **WhatsApp**: `locationMessage.comment` and `liveLocationMessage.caption` are appended as the caption line.
diff --git a/docs/providers/msteams.md b/docs/channels/msteams.md
similarity index 87%
rename from docs/providers/msteams.md
rename to docs/channels/msteams.md
index 416021220..534d261ae 100644
--- a/docs/providers/msteams.md
+++ b/docs/channels/msteams.md
@@ -1,7 +1,7 @@
---
summary: "Microsoft Teams bot support status, capabilities, and configuration"
read_when:
- - Working on MS Teams provider features
+ - Working on MS Teams channel features
---
# Microsoft Teams (Bot Framework)
@@ -21,39 +21,43 @@ Status: text + DM attachments are supported; channel/group attachments require M
Minimal config:
```json5
{
- msteams: {
- enabled: true,
- appId: "",
- appPassword: "",
- tenantId: "",
- webhook: { port: 3978, path: "/api/messages" }
+ channels: {
+ msteams: {
+ enabled: true,
+ appId: "",
+ appPassword: "",
+ tenantId: "",
+ webhook: { port: 3978, path: "/api/messages" }
+ }
}
}
```
-Note: group chats are blocked by default (`msteams.groupPolicy: "allowlist"`). To allow group replies, set `msteams.groupAllowFrom` (or use `groupPolicy: "open"` to allow any member, mention-gated).
+Note: group chats are blocked by default (`channels.msteams.groupPolicy: "allowlist"`). To allow group replies, set `channels.msteams.groupAllowFrom` (or use `groupPolicy: "open"` to allow any member, mention-gated).
## Goals
- Talk to Clawdbot via Teams DMs, group chats, or channels.
-- Keep routing deterministic: replies always go back to the provider they arrived on.
+- Keep routing deterministic: replies always go back to the channel they arrived on.
- Default to safe channel behavior (mentions required unless configured otherwise).
## Access control (DMs + groups)
**DM access**
-- Default: `msteams.dmPolicy = "pairing"`. Unknown senders are ignored until approved.
-- `msteams.allowFrom` accepts AAD object IDs or UPNs.
+- Default: `channels.msteams.dmPolicy = "pairing"`. Unknown senders are ignored until approved.
+- `channels.msteams.allowFrom` accepts AAD object IDs or UPNs.
**Group access**
-- Default: `msteams.groupPolicy = "allowlist"` (blocked unless you add `groupAllowFrom`).
-- `msteams.groupAllowFrom` controls which senders can trigger in group chats/channels (falls back to `msteams.allowFrom`).
+- Default: `channels.msteams.groupPolicy = "allowlist"` (blocked unless you add `groupAllowFrom`).
+- `channels.msteams.groupAllowFrom` controls which senders can trigger in group chats/channels (falls back to `channels.msteams.allowFrom`).
- Set `groupPolicy: "open"` to allow any member (still mention‑gated by default).
Example:
```json5
{
- msteams: {
- groupPolicy: "allowlist",
- groupAllowFrom: ["user@org.com"]
+ channels: {
+ msteams: {
+ groupPolicy: "allowlist",
+ groupAllowFrom: ["user@org.com"]
+ }
}
}
```
@@ -189,10 +193,10 @@ This is often easier than hand-editing JSON manifests.
- `https://:3978/api/messages` (or your chosen path/port).
5. **Run the gateway**
- - The Teams provider starts automatically when `msteams` config exists and credentials are set.
+ - The Teams channel starts automatically when `msteams` config exists and credentials are set.
## History context
-- `msteams.historyLimit` controls how many recent channel/group messages are wrapped into the prompt.
+- `channels.msteams.historyLimit` controls how many recent channel/group messages are wrapped into the prompt.
- Falls back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).
## Current Teams RSC Permissions (Manifest)
@@ -336,22 +340,22 @@ Teams markdown is more limited than Slack or Discord:
- Adaptive Cards are used for polls; other card types are not yet supported
## Configuration
-Key settings (see `/gateway/configuration` for shared provider patterns):
+Key settings (see `/gateway/configuration` for shared channel patterns):
-- `msteams.enabled`: enable/disable the provider.
-- `msteams.appId`, `msteams.appPassword`, `msteams.tenantId`: bot credentials.
-- `msteams.webhook.port` (default `3978`)
-- `msteams.webhook.path` (default `/api/messages`)
-- `msteams.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing)
-- `msteams.allowFrom`: allowlist for DMs (AAD object IDs or UPNs).
-- `msteams.textChunkLimit`: outbound text chunk size.
-- `msteams.mediaAllowHosts`: allowlist for inbound attachment hosts (defaults to Microsoft/Teams domains).
-- `msteams.requireMention`: require @mention in channels/groups (default true).
-- `msteams.replyStyle`: `thread | top-level` (see [Reply Style](#reply-style-threads-vs-posts)).
-- `msteams.teams..replyStyle`: per-team override.
-- `msteams.teams..requireMention`: per-team override.
-- `msteams.teams..channels..replyStyle`: per-channel override.
-- `msteams.teams..channels..requireMention`: per-channel override.
+- `channels.msteams.enabled`: enable/disable the channel.
+- `channels.msteams.appId`, `channels.msteams.appPassword`, `channels.msteams.tenantId`: bot credentials.
+- `channels.msteams.webhook.port` (default `3978`)
+- `channels.msteams.webhook.path` (default `/api/messages`)
+- `channels.msteams.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing)
+- `channels.msteams.allowFrom`: allowlist for DMs (AAD object IDs or UPNs).
+- `channels.msteams.textChunkLimit`: outbound text chunk size.
+- `channels.msteams.mediaAllowHosts`: allowlist for inbound attachment hosts (defaults to Microsoft/Teams domains).
+- `channels.msteams.requireMention`: require @mention in channels/groups (default true).
+- `channels.msteams.replyStyle`: `thread | top-level` (see [Reply Style](#reply-style-threads-vs-posts)).
+- `channels.msteams.teams..replyStyle`: per-team override.
+- `channels.msteams.teams..requireMention`: per-team override.
+- `channels.msteams.teams..channels..replyStyle`: per-channel override.
+- `channels.msteams.teams..channels..requireMention`: per-channel override.
## Routing & Sessions
- Session keys follow the standard agent format (see [/concepts/session](/concepts/session)):
@@ -399,7 +403,7 @@ Teams recently introduced two channel UI styles over the same underlying data mo
- **Channels/groups:** Attachments live in M365 storage (SharePoint/OneDrive). The webhook payload only includes an HTML stub, not the actual file bytes. **Graph API permissions are required** to download channel attachments.
Without Graph permissions, channel messages with images will be received as text-only (the image content is not accessible to the bot).
-By default, Clawdbot only downloads media from Microsoft/Teams hostnames. Override with `msteams.mediaAllowHosts` (use `["*"]` to allow any host).
+By default, Clawdbot only downloads media from Microsoft/Teams hostnames. Override with `channels.msteams.mediaAllowHosts` (use `["*"]` to allow any host).
## Polls (Adaptive Cards)
Clawdbot sends Teams polls as Adaptive Cards (there is no native Teams poll API).
@@ -458,7 +462,7 @@ Bots have limited support in private channels:
### Common issues
- **Images not showing in channels:** Graph permissions or admin consent missing. Reinstall the Teams app and fully quit/reopen Teams.
-- **No responses in channel:** mentions are required by default; set `msteams.requireMention=false` or configure per team/channel.
+- **No responses in channel:** mentions are required by default; set `channels.msteams.requireMention=false` or configure per team/channel.
- **Version mismatch (Teams still shows old manifest):** remove + re-add the app and fully quit Teams to refresh.
- **401 Unauthorized from webhook:** Expected when testing manually without Azure JWT - means endpoint is reachable but auth failed. Use Azure Web Chat to test properly.
diff --git a/docs/channels/signal.md b/docs/channels/signal.md
new file mode 100644
index 000000000..2099bd644
--- /dev/null
+++ b/docs/channels/signal.md
@@ -0,0 +1,123 @@
+---
+summary: "Signal support via signal-cli (JSON-RPC + SSE), setup, and number model"
+read_when:
+ - Setting up Signal support
+ - Debugging Signal send/receive
+---
+# Signal (signal-cli)
+
+
+Status: external CLI integration. Gateway talks to `signal-cli` over HTTP JSON-RPC + SSE.
+
+## Quick setup (beginner)
+1) Use a **separate Signal number** for the bot (recommended).
+2) Install `signal-cli` (Java required).
+3) Link the bot device and start the daemon:
+ - `signal-cli link -n "Clawdbot"`
+4) Configure Clawdbot and start the gateway.
+
+Minimal config:
+```json5
+{
+ channels: {
+ signal: {
+ enabled: true,
+ account: "+15551234567",
+ cliPath: "signal-cli",
+ dmPolicy: "pairing",
+ allowFrom: ["+15557654321"]
+ }
+ }
+}
+```
+
+## What it is
+- Signal channel via `signal-cli` (not embedded libsignal).
+- Deterministic routing: replies always go back to Signal.
+- DMs share the agent's main session; groups are isolated (`agent::signal:group:`).
+
+## The number model (important)
+- The gateway connects to a **Signal device** (the `signal-cli` account).
+- If you run the bot on **your personal Signal account**, it will ignore your own messages (loop protection).
+- For "I text the bot and it replies," use a **separate bot number**.
+
+## Setup (fast path)
+1) Install `signal-cli` (Java required).
+2) Link a bot account:
+ - `signal-cli link -n "Clawdbot"` then scan the QR in Signal.
+3) Configure Signal and start the gateway.
+
+Example:
+```json5
+{
+ channels: {
+ signal: {
+ enabled: true,
+ account: "+15551234567",
+ cliPath: "signal-cli",
+ dmPolicy: "pairing",
+ allowFrom: ["+15557654321"]
+ }
+ }
+}
+```
+
+Multi-account support: use `channels.signal.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
+
+## Access control (DMs + groups)
+DMs:
+- Default: `channels.signal.dmPolicy = "pairing"`.
+- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
+- Approve via:
+ - `clawdbot pairing list signal`
+ - `clawdbot pairing approve signal `
+- Pairing is the default token exchange for Signal DMs. Details: [Pairing](/start/pairing)
+- UUID-only senders (from `sourceUuid`) are stored as `uuid:` in `channels.signal.allowFrom`.
+
+Groups:
+- `channels.signal.groupPolicy = open | allowlist | disabled`.
+- `channels.signal.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
+
+## How it works (behavior)
+- `signal-cli` runs as a daemon; the gateway reads events via SSE.
+- Inbound messages are normalized into the shared channel envelope.
+- Replies always route back to the same number or group.
+
+## Media + limits
+- Outbound text is chunked to `channels.signal.textChunkLimit` (default 4000).
+- Attachments supported (base64 fetched from `signal-cli`).
+- Default media cap: `channels.signal.mediaMaxMb` (default 8).
+- Use `channels.signal.ignoreAttachments` to skip downloading media.
+- Group history context uses `channels.signal.historyLimit` (or `channels.signal.accounts.*.historyLimit`), falling back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).
+
+## Delivery targets (CLI/cron)
+- DMs: `signal:+15551234567` (or plain E.164).
+- Groups: `signal:group:`.
+- Usernames: `username:` (if supported by your Signal account).
+
+## Configuration reference (Signal)
+Full configuration: [Configuration](/gateway/configuration)
+
+Provider options:
+- `channels.signal.enabled`: enable/disable channel startup.
+- `channels.signal.account`: E.164 for the bot account.
+- `channels.signal.cliPath`: path to `signal-cli`.
+- `channels.signal.httpUrl`: full daemon URL (overrides host/port).
+- `channels.signal.httpHost`, `channels.signal.httpPort`: daemon bind (default 127.0.0.1:8080).
+- `channels.signal.autoStart`: auto-spawn daemon (default true if `httpUrl` unset).
+- `channels.signal.receiveMode`: `on-start | manual`.
+- `channels.signal.ignoreAttachments`: skip attachment downloads.
+- `channels.signal.ignoreStories`: ignore stories from the daemon.
+- `channels.signal.sendReadReceipts`: forward read receipts.
+- `channels.signal.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
+- `channels.signal.allowFrom`: DM allowlist (E.164 or `uuid:`). `open` requires `"*"`.
+- `channels.signal.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
+- `channels.signal.groupAllowFrom`: group sender allowlist.
+- `channels.signal.historyLimit`: max group messages to include as context (0 disables).
+- `channels.signal.textChunkLimit`: outbound chunk size (chars).
+- `channels.signal.mediaMaxMb`: inbound/outbound media cap (MB).
+
+Related global options:
+- `agents.list[].groupChat.mentionPatterns` (Signal does not support native mentions).
+- `messages.groupChat.mentionPatterns` (global fallback).
+- `messages.responsePrefix`.
diff --git a/docs/providers/slack.md b/docs/channels/slack.md
similarity index 68%
rename from docs/providers/slack.md
rename to docs/channels/slack.md
index dfba65c26..87b0613e1 100644
--- a/docs/providers/slack.md
+++ b/docs/channels/slack.md
@@ -13,16 +13,18 @@ read_when: "Setting up Slack or debugging Slack socket mode"
Minimal config:
```json5
{
- slack: {
- enabled: true,
- appToken: "xapp-...",
- botToken: "xoxb-..."
+ channels: {
+ slack: {
+ enabled: true,
+ appToken: "xapp-...",
+ botToken: "xoxb-..."
+ }
}
}
```
## Setup
-1) Create a Slack app (From scratch) in https://api.slack.com/apps.
+1) Create a Slack app (From scratch) in https://api.channels.slack.com/apps.
2) **Socket Mode** → toggle on. Then go to **Basic Information** → **App-Level Tokens** → **Generate Token and Scopes** with scope `connections:write`. Copy the **App Token** (`xapp-...`).
3) **OAuth & Permissions** → add bot token scopes (use the manifest below). Click **Install to Workspace**. Copy the **Bot User OAuth Token** (`xoxb-...`).
4) **Event Subscriptions** → enable events and subscribe to:
@@ -33,12 +35,12 @@ Minimal config:
- `channel_rename`
- `pin_added`, `pin_removed`
5) Invite the bot to channels you want it to read.
-6) Slash Commands → create `/clawd` if you use `slack.slashCommand`. If you enable native commands, add one slash command per built-in command (same names as `/help`). Native defaults to off for Slack unless you set `slack.commands.native: true` (global `commands.native` is `"auto"` which leaves Slack off).
+6) Slash Commands → create `/clawd` if you use `channels.slack.slashCommand`. If you enable native commands, add one slash command per built-in command (same names as `/help`). Native defaults to off for Slack unless you set `channels.slack.commands.native: true` (global `commands.native` is `"auto"` which leaves Slack off).
7) App Home → enable the **Messages Tab** so users can DM the bot.
Use the manifest below so scopes and events stay in sync.
-Multi-account support: use `slack.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
+Multi-account support: use `channels.slack.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
## Clawdbot config (minimal)
@@ -50,16 +52,18 @@ Or via config:
```json5
{
- slack: {
- enabled: true,
- appToken: "xapp-...",
- botToken: "xoxb-..."
+ channels: {
+ slack: {
+ enabled: true,
+ appToken: "xapp-...",
+ botToken: "xoxb-..."
+ }
}
}
```
## History context
-- `slack.historyLimit` (or `slack.accounts.*.historyLimit`) controls how many recent channel/group messages are wrapped into the prompt.
+- `channels.slack.historyLimit` (or `channels.slack.accounts.*.historyLimit`) controls how many recent channel/group messages are wrapped into the prompt.
- Falls back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).
## Manifest (optional)
@@ -138,42 +142,42 @@ Use this Slack app manifest to create the app quickly (adjust the name/command i
}
```
-If you enable native commands, add one `slash_commands` entry per command you want to expose (matching the `/help` list). Override with `slack.commands.native`.
+If you enable native commands, add one `slash_commands` entry per command you want to expose (matching the `/help` list). Override with `channels.slack.commands.native`.
## Scopes (current vs optional)
Slack's Conversations API is type-scoped: you only need the scopes for the
conversation types you actually touch (channels, groups, im, mpim). See
-https://api.slack.com/docs/conversations-api for the overview.
+https://api.channels.slack.com/docs/conversations-api for the overview.
### Required scopes
- `chat:write` (send/update/delete messages via `chat.postMessage`)
- https://api.slack.com/methods/chat.postMessage
+ https://api.channels.slack.com/methods/chat.postMessage
- `im:write` (open DMs via `conversations.open` for user DMs)
- https://api.slack.com/methods/conversations.open
+ https://api.channels.slack.com/methods/conversations.open
- `channels:history`, `groups:history`, `im:history`, `mpim:history`
- https://api.slack.com/methods/conversations.history
+ https://api.channels.slack.com/methods/conversations.history
- `channels:read`, `groups:read`, `im:read`, `mpim:read`
- https://api.slack.com/methods/conversations.info
+ https://api.channels.slack.com/methods/conversations.info
- `users:read` (user lookup)
- https://api.slack.com/methods/users.info
+ https://api.channels.slack.com/methods/users.info
- `reactions:read`, `reactions:write` (`reactions.get` / `reactions.add`)
- https://api.slack.com/methods/reactions.get
- https://api.slack.com/methods/reactions.add
+ https://api.channels.slack.com/methods/reactions.get
+ https://api.channels.slack.com/methods/reactions.add
- `pins:read`, `pins:write` (`pins.list` / `pins.add` / `pins.remove`)
- https://api.slack.com/scopes/pins:read
- https://api.slack.com/scopes/pins:write
+ https://api.channels.slack.com/scopes/pins:read
+ https://api.channels.slack.com/scopes/pins:write
- `emoji:read` (`emoji.list`)
- https://api.slack.com/scopes/emoji:read
+ https://api.channels.slack.com/scopes/emoji:read
- `files:write` (uploads via `files.uploadV2`)
- https://api.slack.com/messaging/files/uploading
+ https://api.channels.slack.com/messaging/files/uploading
### Not needed today (but likely future)
- `mpim:write` (only if we add group-DM open/DM start via `conversations.open`)
- `groups:write` (only if we add private-channel management: create/rename/invite/archive)
- `chat:write.public` (only if we want to post to channels the bot isn't in)
- https://api.slack.com/scopes/chat:write.public
+ https://api.channels.slack.com/scopes/chat:write.public
- `users:read.email` (only if we need email fields from `users.info`)
- https://api.slack.com/changelog/2017-04-narrowing-email-access
+ https://api.channels.slack.com/changelog/2017-04-narrowing-email-access
- `files:read` (only if we start listing/reading file metadata)
## Config
@@ -234,11 +238,11 @@ Ack reactions are controlled globally via `messages.ackReaction` +
ack reaction after the bot replies.
## Limits
-- Outbound text is chunked to `slack.textChunkLimit` (default 4000).
-- Media uploads are capped by `slack.mediaMaxMb` (default 20).
+- Outbound text is chunked to `channels.slack.textChunkLimit` (default 4000).
+- Media uploads are capped by `channels.slack.mediaMaxMb` (default 20).
## Reply threading
-By default, Clawdbot replies in the main channel. Use `slack.replyToMode` to control automatic threading:
+By default, Clawdbot replies in the main channel. Use `channels.slack.replyToMode` to control automatic threading:
| Mode | Behavior |
| --- | --- |
@@ -256,20 +260,20 @@ For fine-grained control, use these tags in agent responses:
## Sessions + routing
- DMs share the `main` session (like WhatsApp/Telegram).
- Channels map to `agent::slack:channel:` sessions.
-- Slash commands use `agent::slack:slash:` sessions (prefix configurable via `slack.slashCommand.sessionPrefix`).
-- Native command registration uses `commands.native` (global default `"auto"` → Slack off) and can be overridden per-workspace with `slack.commands.native`. Text commands require standalone `/...` messages and can be disabled with `commands.text: false`. Slack slash commands are managed in the Slack app and are not removed automatically. Use `commands.useAccessGroups: false` to bypass access-group checks for commands.
+- Slash commands use `agent::slack:slash:` sessions (prefix configurable via `channels.slack.slashCommand.sessionPrefix`).
+- Native command registration uses `commands.native` (global default `"auto"` → Slack off) and can be overridden per-workspace with `channels.slack.commands.native`. Text commands require standalone `/...` messages and can be disabled with `commands.text: false`. Slack slash commands are managed in the Slack app and are not removed automatically. Use `commands.useAccessGroups: false` to bypass access-group checks for commands.
- Full command list + config: [Slash commands](/tools/slash-commands)
## DM security (pairing)
-- Default: `slack.dm.policy="pairing"` — unknown DM senders get a pairing code (expires after 1 hour).
+- Default: `channels.slack.dm.policy="pairing"` — unknown DM senders get a pairing code (expires after 1 hour).
- Approve via: `clawdbot pairing approve slack `.
-- To allow anyone: set `slack.dm.policy="open"` and `slack.dm.allowFrom=["*"]`.
+- To allow anyone: set `channels.slack.dm.policy="open"` and `channels.slack.dm.allowFrom=["*"]`.
## Group policy
-- `slack.groupPolicy` controls channel handling (`open|disabled|allowlist`).
-- `allowlist` requires channels to be listed in `slack.channels`.
+- `channels.slack.groupPolicy` controls channel handling (`open|disabled|allowlist`).
+- `allowlist` requires channels to be listed in `channels.slack.channels`.
-Channel options (`slack.channels.` or `slack.channels.`):
+Channel options (`channels.slack.channels.` or `channels.slack.channels.`):
- `allow`: allow/deny the channel when `groupPolicy="allowlist"`.
- `requireMention`: mention gating for the channel.
- `allowBots`: allow bot-authored messages in this channel (default: false).
@@ -284,7 +288,7 @@ Use these with cron/CLI sends:
- `channel:` for channels
## Tool actions
-Slack tool actions can be gated with `slack.actions.*`:
+Slack tool actions can be gated with `channels.slack.actions.*`:
| Action group | Default | Notes |
| --- | --- | --- |
@@ -295,10 +299,10 @@ Slack tool actions can be gated with `slack.actions.*`:
| emojiList | enabled | Custom emoji list |
## Notes
-- Mention gating is controlled via `slack.channels` (set `requireMention` to `true`); `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) also count as mentions.
+- Mention gating is controlled via `channels.slack.channels` (set `requireMention` to `true`); `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) also count as mentions.
- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.
-- Reaction notifications follow `slack.reactionNotifications` (use `reactionAllowlist` with mode `allowlist`).
-- Bot-authored messages are ignored by default; enable via `slack.allowBots` or `slack.channels..allowBots`.
-- Warning: If you allow replies to other bots (`slack.allowBots=true` or `slack.channels..allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `slack.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`.
+- Reaction notifications follow `channels.slack.reactionNotifications` (use `reactionAllowlist` with mode `allowlist`).
+- Bot-authored messages are ignored by default; enable via `channels.slack.allowBots` or `channels.slack.channels..allowBots`.
+- Warning: If you allow replies to other bots (`channels.slack.allowBots=true` or `channels.slack.channels..allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `channels.slack.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`.
- For the Slack tool, reaction removal semantics are in [/tools/reactions](/tools/reactions).
- Attachments are downloaded to the media store when permitted and under the size limit.
diff --git a/docs/providers/telegram.md b/docs/channels/telegram.md
similarity index 60%
rename from docs/providers/telegram.md
rename to docs/channels/telegram.md
index 325b5c598..dd83a3ba4 100644
--- a/docs/providers/telegram.md
+++ b/docs/channels/telegram.md
@@ -12,24 +12,26 @@ Status: production-ready for bot DMs + groups via grammY. Long-polling by defaul
1) Create a bot with **@BotFather** and copy the token.
2) Set the token:
- Env: `TELEGRAM_BOT_TOKEN=...`
- - Or config: `telegram.botToken: "..."`.
+ - Or config: `channels.telegram.botToken: "..."`.
3) Start the gateway.
4) DM access is pairing by default; approve the pairing code on first contact.
Minimal config:
```json5
{
- telegram: {
- enabled: true,
- botToken: "123:abc",
- dmPolicy: "pairing"
+ channels: {
+ telegram: {
+ enabled: true,
+ botToken: "123:abc",
+ dmPolicy: "pairing"
+ }
}
}
```
## What it is
-- A Telegram Bot API provider owned by the Gateway.
-- Deterministic routing: replies go back to Telegram; the model never chooses providers.
+- A Telegram Bot API channel owned by the Gateway.
+- Deterministic routing: replies go back to Telegram; the model never chooses channels.
- DMs share the agent's main session; groups stay isolated (`agent::telegram:group:`).
## Setup (fast path)
@@ -47,22 +49,24 @@ Example:
```json5
{
- telegram: {
- enabled: true,
- botToken: "123:abc",
- dmPolicy: "pairing",
- groups: { "*": { requireMention: true } }
+ channels: {
+ telegram: {
+ enabled: true,
+ botToken: "123:abc",
+ dmPolicy: "pairing",
+ groups: { "*": { requireMention: true } }
+ }
}
}
```
Env option: `TELEGRAM_BOT_TOKEN=...` (works for the default account).
-Multi-account support: use `telegram.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
+Multi-account support: use `channels.telegram.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.
3) Start the gateway. Telegram starts when a token is resolved (env or config).
4) DM access defaults to pairing. Approve the code when the bot is first contacted.
-5) For groups: add the bot, decide privacy/admin behavior (below), then set `telegram.groups` to control mention gating + allowlists.
+5) For groups: add the bot, decide privacy/admin behavior (below), then set `channels.telegram.groups` to control mention gating + allowlists.
## Token + privacy + permissions (Telegram side)
@@ -84,7 +88,7 @@ Admin status is set inside the group (Telegram UI). Admin bots always receive al
group messages, so use admin if you need full visibility.
## How it works (behavior)
-- Inbound messages are normalized into the shared provider envelope with reply context and media placeholders.
+- Inbound messages are normalized into the shared channel envelope with reply context and media placeholders.
- Group replies require a mention by default (native @mention or `agents.list[].groupChat.mentionPatterns` / `messages.groupChat.mentionPatterns`).
- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.
- Replies always route back to the same Telegram chat.
@@ -97,9 +101,9 @@ group messages, so use admin if you need full visibility.
- If Telegram rejects the HTML payload, Clawdbot retries the same message as plain text.
## Limits
-- Outbound text is chunked to `telegram.textChunkLimit` (default 4000).
-- Media downloads/uploads are capped by `telegram.mediaMaxMb` (default 5).
-- Group history context uses `telegram.historyLimit` (or `telegram.accounts.*.historyLimit`), falling back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).
+- Outbound text is chunked to `channels.telegram.textChunkLimit` (default 4000).
+- Media downloads/uploads are capped by `channels.telegram.mediaMaxMb` (default 5).
+- Group history context uses `channels.telegram.historyLimit` (or `channels.telegram.accounts.*.historyLimit`), falling back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).
## Group activation modes
@@ -109,22 +113,26 @@ By default, the bot only responds to mentions in groups (`@botname` or patterns
```json5
{
- telegram: {
- groups: {
- "-1001234567890": { requireMention: false } // always respond in this group
+ channels: {
+ telegram: {
+ groups: {
+ "-1001234567890": { requireMention: false } // always respond in this group
+ }
}
}
}
```
-**Important:** Setting `telegram.groups` creates an **allowlist** - only listed groups (or `"*"`) will be accepted.
+**Important:** Setting `channels.telegram.groups` creates an **allowlist** - only listed groups (or `"*"`) will be accepted.
To allow all groups with always-respond:
```json5
{
- telegram: {
- groups: {
- "*": { requireMention: false } // all groups, always respond
+ channels: {
+ telegram: {
+ groups: {
+ "*": { requireMention: false } // all groups, always respond
+ }
}
}
}
@@ -133,9 +141,11 @@ To allow all groups with always-respond:
To keep mention-only for all groups (default behavior):
```json5
{
- telegram: {
- groups: {
- "*": { requireMention: true } // or omit groups entirely
+ channels: {
+ telegram: {
+ groups: {
+ "*": { requireMention: true } // or omit groups entirely
+ }
}
}
}
@@ -162,49 +172,49 @@ Telegram forum topics include a `message_thread_id` per message. Clawdbot:
- Appends `:topic:` to the Telegram group session key so each topic is isolated.
- Sends typing indicators and replies with `message_thread_id` so responses stay in the topic.
- Exposes `MessageThreadId` + `IsForum` in template context for routing/templating.
-- Topic-specific configuration is available under `telegram.groups..topics.` (skills, allowlists, auto-reply, system prompts, disable).
+- Topic-specific configuration is available under `channels.telegram.groups..topics.` (skills, allowlists, auto-reply, system prompts, disable).
Private chats can include `message_thread_id` in some edge cases. Clawdbot keeps the DM session key unchanged, but still uses the thread id for replies/draft streaming when it is present.
## Access control (DMs + groups)
### DM access
-- Default: `telegram.dmPolicy = "pairing"`. Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
+- Default: `channels.telegram.dmPolicy = "pairing"`. Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
- Approve via:
- `clawdbot pairing list telegram`
- `clawdbot pairing approve telegram `
- Pairing is the default token exchange used for Telegram DMs. Details: [Pairing](/start/pairing)
-- `telegram.allowFrom` accepts numeric user IDs (recommended) or `@username` entries. It is **not** the bot username; use the human sender’s ID (get it from `@userinfobot` or the `from.id` field in the gateway log).
+- `channels.telegram.allowFrom` accepts numeric user IDs (recommended) or `@username` entries. It is **not** the bot username; use the human sender’s ID (get it from `@userinfobot` or the `from.id` field in the gateway log).
### Group access
Two independent controls:
-**1. Which groups are allowed** (group allowlist via `telegram.groups`):
+**1. Which groups are allowed** (group allowlist via `channels.telegram.groups`):
- No `groups` config = all groups allowed
- With `groups` config = only listed groups or `"*"` are allowed
- Example: `"groups": { "-1001234567890": {}, "*": {} }` allows all groups
-**2. Which senders are allowed** (sender filtering via `telegram.groupPolicy`):
+**2. Which senders are allowed** (sender filtering via `channels.telegram.groupPolicy`):
- `"open"` = all senders in allowed groups can message
-- `"allowlist"` = only senders in `telegram.groupAllowFrom` can message
+- `"allowlist"` = only senders in `channels.telegram.groupAllowFrom` can message
- `"disabled"` = no group messages accepted at all
Default is `groupPolicy: "allowlist"` (blocked unless you add `groupAllowFrom`).
-Most users want: `groupPolicy: "allowlist"` + `groupAllowFrom` + specific groups listed in `telegram.groups`
+Most users want: `groupPolicy: "allowlist"` + `groupAllowFrom` + specific groups listed in `channels.telegram.groups`
## Long-polling vs webhook
- Default: long-polling (no public URL required).
-- Webhook mode: set `telegram.webhookUrl` (optionally `telegram.webhookSecret` + `telegram.webhookPath`).
+- Webhook mode: set `channels.telegram.webhookUrl` (optionally `channels.telegram.webhookSecret` + `channels.telegram.webhookPath`).
- The local listener binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.
- - If your public URL is different, use a reverse proxy and point `telegram.webhookUrl` at the public endpoint.
+ - If your public URL is different, use a reverse proxy and point `channels.telegram.webhookUrl` at the public endpoint.
## Reply threading
Telegram supports optional threaded replies via tags:
- `[[reply_to_current]]` -- reply to the triggering message.
- `[[reply_to:]]` -- reply to a specific message id.
-Controlled by `telegram.replyToMode`:
+Controlled by `channels.telegram.replyToMode`:
- `first` (default), `all`, `off`.
## Audio messages (voice vs file)
@@ -214,7 +224,7 @@ Clawdbot defaults to audio files for backward compatibility.
To force a voice note bubble in agent replies, include this tag anywhere in the reply:
- `[[audio_as_voice]]` — send audio as a voice note instead of a file.
-The tag is stripped from the delivered text. Other providers ignore this tag.
+The tag is stripped from the delivered text. Other channels ignore this tag.
## Streaming (drafts)
Telegram can stream **draft bubbles** while the agent is generating a response.
@@ -227,32 +237,32 @@ Requirements (Telegram Bot API 9.3+):
- Streaming is ignored for groups/supergroups/channels.
Config:
-- `telegram.streamMode: "off" | "partial" | "block"` (default: `partial`)
+- `channels.telegram.streamMode: "off" | "partial" | "block"` (default: `partial`)
- `partial`: update the draft bubble with the latest streaming text.
- `block`: update the draft bubble in larger blocks (chunked).
- `off`: disable draft streaming.
- Optional (only for `streamMode: "block"`):
- - `telegram.draftChunk: { minChars?, maxChars?, breakPreference? }`
- - defaults: `minChars: 200`, `maxChars: 800`, `breakPreference: "paragraph"` (clamped to `telegram.textChunkLimit`).
+ - `channels.telegram.draftChunk: { minChars?, maxChars?, breakPreference? }`
+ - defaults: `minChars: 200`, `maxChars: 800`, `breakPreference: "paragraph"` (clamped to `channels.telegram.textChunkLimit`).
-Note: draft streaming is separate from **block streaming** (provider messages).
-Block streaming is off by default and requires `telegram.blockStreaming: true`
+Note: draft streaming is separate from **block streaming** (channel messages).
+Block streaming is off by default and requires `channels.telegram.blockStreaming: true`
if you want early Telegram messages instead of draft updates.
Reasoning stream (Telegram only):
- `/reasoning stream` streams reasoning into the draft bubble while the reply is
generating, then sends the final answer without reasoning.
-- If `telegram.streamMode` is `off`, reasoning stream is disabled.
+- If `channels.telegram.streamMode` is `off`, reasoning stream is disabled.
More context: [Streaming + chunking](/concepts/streaming).
## Retry policy
-Outbound Telegram API calls retry on transient network/429 errors with exponential backoff and jitter. Configure via `telegram.retry`. See [Retry policy](/concepts/retry).
+Outbound Telegram API calls retry on transient network/429 errors with exponential backoff and jitter. Configure via `channels.telegram.retry`. See [Retry policy](/concepts/retry).
## Agent tool (messages + reactions)
- Tool: `telegram` with `sendMessage` action (`to`, `content`, optional `mediaUrl`, `replyToMessageId`, `messageThreadId`).
- Tool: `telegram` with `react` action (`chatId`, `messageId`, `emoji`).
- Reaction removal semantics: see [/tools/reactions](/tools/reactions).
-- Tool gating: `telegram.actions.reactions` and `telegram.actions.sendMessage` (default: enabled).
+- Tool gating: `channels.telegram.actions.reactions` and `channels.telegram.actions.sendMessage` (default: enabled).
## Delivery targets (CLI/cron)
- Use a chat id (`123456789`) or a username (`@name`) as the target.
@@ -261,59 +271,59 @@ Outbound Telegram API calls retry on transient network/429 errors with exponenti
## Troubleshooting
**Bot doesn’t respond to non-mention messages in a group:**
-- If you set `telegram.groups.*.requireMention=false`, Telegram’s Bot API **privacy mode** must be disabled.
+- If you set `channels.telegram.groups.*.requireMention=false`, Telegram’s Bot API **privacy mode** must be disabled.
- BotFather: `/setprivacy` → **Disable** (then remove + re-add the bot to the group)
-- `clawdbot providers status` shows a warning when config expects unmentioned group messages.
-- `clawdbot providers status --probe` can additionally check membership for explicit numeric group IDs (it can’t audit wildcard `"*"` rules).
+- `clawdbot channels status` shows a warning when config expects unmentioned group messages.
+- `clawdbot channels status --probe` can additionally check membership for explicit numeric group IDs (it can’t audit wildcard `"*"` rules).
- Quick test: `/activation always` (session-only; use config for persistence)
**Bot not seeing group messages at all:**
-- If `telegram.groups` is set, the group must be listed or use `"*"`
+- If `channels.telegram.groups` is set, the group must be listed or use `"*"`
- Check Privacy Settings in @BotFather → "Group Privacy" should be **OFF**
- Verify bot is actually a member (not just an admin with no read access)
- Check gateway logs: `clawdbot logs --follow` (look for "skipping group message")
**Bot responds to mentions but not `/activation always`:**
- The `/activation` command updates session state but doesn't persist to config
-- For persistent behavior, add group to `telegram.groups` with `requireMention: false`
+- For persistent behavior, add group to `channels.telegram.groups` with `requireMention: false`
**Commands like `/status` don't work:**
-- Make sure your Telegram user ID is authorized (via pairing or `telegram.allowFrom`)
+- Make sure your Telegram user ID is authorized (via pairing or `channels.telegram.allowFrom`)
- Commands require authorization even in groups with `groupPolicy: "open"`
## Configuration reference (Telegram)
Full configuration: [Configuration](/gateway/configuration)
Provider options:
-- `telegram.enabled`: enable/disable provider startup.
-- `telegram.botToken`: bot token (BotFather).
-- `telegram.tokenFile`: read token from file path.
-- `telegram.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
-- `telegram.allowFrom`: DM allowlist (ids/usernames). `open` requires `"*"`.
-- `telegram.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
-- `telegram.groupAllowFrom`: group sender allowlist (ids/usernames).
-- `telegram.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
- - `telegram.groups..requireMention`: mention gating default.
- - `telegram.groups..skills`: skill filter (omit = all skills, empty = none).
- - `telegram.groups..allowFrom`: per-group sender allowlist override.
- - `telegram.groups..systemPrompt`: extra system prompt for the group.
- - `telegram.groups..enabled`: disable the group when `false`.
- - `telegram.groups..topics..*`: per-topic overrides (same fields as group).
- - `telegram.groups..topics..requireMention`: per-topic mention gating override.
-- `telegram.replyToMode`: `off | first | all` (default: `first`).
-- `telegram.textChunkLimit`: outbound chunk size (chars).
-- `telegram.streamMode`: `off | partial | block` (draft streaming).
-- `telegram.mediaMaxMb`: inbound/outbound media cap (MB).
-- `telegram.retry`: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter).
-- `telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP).
-- `telegram.webhookUrl`: enable webhook mode.
-- `telegram.webhookSecret`: webhook secret (optional).
-- `telegram.webhookPath`: local webhook path (default `/telegram-webhook`).
-- `telegram.actions.reactions`: gate Telegram tool reactions.
-- `telegram.actions.sendMessage`: gate Telegram tool message sends.
+- `channels.telegram.enabled`: enable/disable channel startup.
+- `channels.telegram.botToken`: bot token (BotFather).
+- `channels.telegram.tokenFile`: read token from file path.
+- `channels.telegram.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).
+- `channels.telegram.allowFrom`: DM allowlist (ids/usernames). `open` requires `"*"`.
+- `channels.telegram.groupPolicy`: `open | allowlist | disabled` (default: allowlist).
+- `channels.telegram.groupAllowFrom`: group sender allowlist (ids/usernames).
+- `channels.telegram.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
+ - `channels.telegram.groups..requireMention`: mention gating default.
+ - `channels.telegram.groups..skills`: skill filter (omit = all skills, empty = none).
+ - `channels.telegram.groups..allowFrom`: per-group sender allowlist override.
+ - `channels.telegram.groups..systemPrompt`: extra system prompt for the group.
+ - `channels.telegram.groups..enabled`: disable the group when `false`.
+ - `channels.telegram.groups..topics..*`: per-topic overrides (same fields as group).
+ - `channels.telegram.groups..topics..requireMention`: per-topic mention gating override.
+- `channels.telegram.replyToMode`: `off | first | all` (default: `first`).
+- `channels.telegram.textChunkLimit`: outbound chunk size (chars).
+- `channels.telegram.streamMode`: `off | partial | block` (draft streaming).
+- `channels.telegram.mediaMaxMb`: inbound/outbound media cap (MB).
+- `channels.telegram.retry`: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter).
+- `channels.telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP).
+- `channels.telegram.webhookUrl`: enable webhook mode.
+- `channels.telegram.webhookSecret`: webhook secret (optional).
+- `channels.telegram.webhookPath`: local webhook path (default `/telegram-webhook`).
+- `channels.telegram.actions.reactions`: gate Telegram tool reactions.
+- `channels.telegram.actions.sendMessage`: gate Telegram tool message sends.
Related global options:
- `agents.list[].groupChat.mentionPatterns` (mention gating patterns).
- `messages.groupChat.mentionPatterns` (global fallback).
-- `commands.native` (defaults to `"auto"` → on for Telegram/Discord, off for Slack), `commands.text`, `commands.useAccessGroups` (command behavior). Override with `telegram.commands.native`.
+- `commands.native` (defaults to `"auto"` → on for Telegram/Discord, off for Slack), `commands.text`, `commands.useAccessGroups` (command behavior). Override with `channels.telegram.commands.native`.
- `messages.responsePrefix`, `messages.ackReaction`, `messages.ackReactionScope`, `messages.removeAckAfterReply`.
diff --git a/docs/channels/troubleshooting.md b/docs/channels/troubleshooting.md
new file mode 100644
index 000000000..d40a92af2
--- /dev/null
+++ b/docs/channels/troubleshooting.md
@@ -0,0 +1,21 @@
+---
+summary: "Channel-specific troubleshooting shortcuts (Discord/Telegram/WhatsApp)"
+read_when:
+ - A channel connects but messages don’t flow
+ - Investigating channel misconfiguration (intents, permissions, privacy mode)
+---
+# Channel troubleshooting
+
+Start with:
+
+```bash
+clawdbot doctor
+clawdbot channels status --probe
+```
+
+`channels status --probe` prints warnings when it can detect common channel misconfigurations, and includes small live checks (credentials, some permissions/membership).
+
+## Channels
+- Discord: [/channels/discord#troubleshooting](/channels/discord#troubleshooting)
+- Telegram: [/channels/telegram#troubleshooting](/channels/telegram#troubleshooting)
+- WhatsApp: [/channels/whatsapp#troubleshooting-quick](/channels/whatsapp#troubleshooting-quick)
diff --git a/docs/providers/whatsapp.md b/docs/channels/whatsapp.md
similarity index 76%
rename from docs/providers/whatsapp.md
rename to docs/channels/whatsapp.md
index 4a2462fc9..1fc619c23 100644
--- a/docs/providers/whatsapp.md
+++ b/docs/channels/whatsapp.md
@@ -1,9 +1,9 @@
---
-summary: "WhatsApp (web provider) integration: login, inbox, replies, media, and ops"
+summary: "WhatsApp (web channel) integration: login, inbox, replies, media, and ops"
read_when:
- - Working on WhatsApp/web provider behavior or inbox routing
+ - Working on WhatsApp/web channel behavior or inbox routing
---
-# WhatsApp (web provider)
+# WhatsApp (web channel)
Status: WhatsApp Web via Baileys only. Gateway owns the session(s).
@@ -11,15 +11,17 @@ Status: WhatsApp Web via Baileys only. Gateway owns the session(s).
## Quick setup (beginner)
1) Use a **separate phone number** if possible (recommended).
2) Configure WhatsApp in `~/.clawdbot/clawdbot.json`.
-3) Run `clawdbot providers login` to scan the QR code (Linked Devices).
+3) Run `clawdbot channels login` to scan the QR code (Linked Devices).
4) Start the gateway.
Minimal config:
```json5
{
- whatsapp: {
- dmPolicy: "allowlist",
- allowFrom: ["+15551234567"]
+ channels: {
+ whatsapp: {
+ dmPolicy: "allowlist",
+ allowFrom: ["+15551234567"]
+ }
}
}
```
@@ -44,17 +46,19 @@ Use a **separate phone number** for Clawdbot. Best UX, clean routing, no self-ch
**WhatsApp Business:** You can use WhatsApp Business on the same device with a different number. Great for keeping your personal WhatsApp separate — install WhatsApp Business and register the Clawdbot number there.
**Sample config (dedicated number, single-user allowlist):**
-```json
+```json5
{
- "whatsapp": {
- "dmPolicy": "allowlist",
- "allowFrom": ["+15551234567"]
+ channels: {
+ whatsapp: {
+ dmPolicy: "allowlist",
+ allowFrom: ["+15551234567"]
+ }
}
}
```
**Pairing mode (optional):**
-If you want pairing instead of allowlist, set `whatsapp.dmPolicy` to `pairing`. Unknown senders get a pairing code; approve with:
+If you want pairing instead of allowlist, set `channels.whatsapp.dmPolicy` to `pairing`. Unknown senders get a pairing code; approve with:
`clawdbot pairing approve whatsapp `
### Personal number (fallback)
@@ -96,13 +100,13 @@ on outbound replies.
- Result: unreliable delivery and frequent blocks, so support was removed.
## Login + credentials
-- Login command: `clawdbot providers login` (QR via Linked Devices).
-- Multi-account login: `clawdbot providers login --account ` (`` = `accountId`).
+- Login command: `clawdbot channels login` (QR via Linked Devices).
+- Multi-account login: `clawdbot channels login --account ` (`` = `accountId`).
- Default account (when `--account` is omitted): `default` if present, otherwise the first configured account id (sorted).
- Credentials stored in `~/.clawdbot/credentials/whatsapp//creds.json`.
- Backup copy at `creds.json.bak` (restored on corruption).
- Legacy compatibility: older installs stored Baileys files directly in `~/.clawdbot/credentials/`.
-- Logout: `clawdbot providers logout` (or `--account `) deletes WhatsApp auth state (but keeps shared `oauth.json`).
+- Logout: `clawdbot channels logout` (or `--account `) deletes WhatsApp auth state (but keeps shared `oauth.json`).
- Logged-out socket => error instructs re-link.
## Inbound flow (DM + group)
@@ -110,17 +114,17 @@ on outbound replies.
- Inbox listeners are detached on shutdown to avoid accumulating event handlers in tests/restarts.
- Status/broadcast chats are ignored.
- Direct chats use E.164; groups use group JID.
-- **DM policy**: `whatsapp.dmPolicy` controls direct chat access (default: `pairing`).
+- **DM policy**: `channels.whatsapp.dmPolicy` controls direct chat access (default: `pairing`).
- Pairing: unknown senders get a pairing code (approve via `clawdbot pairing approve whatsapp `; codes expire after 1 hour).
- - Open: requires `whatsapp.allowFrom` to include `"*"`.
- - Self messages are always allowed; “self-chat mode” still requires `whatsapp.allowFrom` to include your own number.
+ - Open: requires `channels.whatsapp.allowFrom` to include `"*"`.
+ - Self messages are always allowed; “self-chat mode” still requires `channels.whatsapp.allowFrom` to include your own number.
### Personal-number mode (fallback)
-If you run Clawdbot on your **personal WhatsApp number**, enable `whatsapp.selfChatMode` (see sample above).
+If you run Clawdbot on your **personal WhatsApp number**, enable `channels.whatsapp.selfChatMode` (see sample above).
Behavior:
- Outbound DMs never trigger pairing replies (prevents spamming contacts).
-- Inbound unknown senders still follow `whatsapp.dmPolicy`.
+- Inbound unknown senders still follow `channels.whatsapp.dmPolicy`.
- Self-chat mode (allowFrom includes your number) avoids auto read receipts and ignores mention JIDs.
- Read receipts sent for non-self-chat DMs.
@@ -133,13 +137,13 @@ No. Default DM policy is **pairing**, so unknown senders only get a pairing code
Pairing is a DM gate for unknown senders:
- First DM from a new sender returns a short code (message is not processed).
- Approve with: `clawdbot pairing approve whatsapp ` (list with `clawdbot pairing list whatsapp`).
-- Codes expire after 1 hour; pending requests are capped at 3 per provider.
+- Codes expire after 1 hour; pending requests are capped at 3 per channel.
**Can multiple people use different Clawdbots on one WhatsApp number?**
Yes, by routing each sender to a different agent via `bindings` (peer `kind: "dm"`, sender E.164 like `+15551234567`). Replies still come from the **same WhatsApp account**, and direct chats collapse to each agent’s main session, so use **one agent per person**. DM access control (`dmPolicy`/`allowFrom`) is global per WhatsApp account. See [Multi-Agent Routing](/concepts/multi-agent).
**Why do you ask for my phone number in the wizard?**
-The wizard uses it to set your **allowlist/owner** so your own DMs are permitted. It’s not used for auto-sending. If you run on your personal WhatsApp number, use that same number and enable `whatsapp.selfChatMode`.
+The wizard uses it to set your **allowlist/owner** so your own DMs are permitted. It’s not used for auto-sending. If you run on your personal WhatsApp number, use that same number and enable `channels.whatsapp.selfChatMode`.
## Message normalization (what the model sees)
- `Body` is the current message body with envelope.
@@ -158,12 +162,12 @@ The wizard uses it to set your **allowlist/owner** so your own DMs are permitted
## Groups
- Groups map to `agent::whatsapp:group:` sessions.
-- Group policy: `whatsapp.groupPolicy = open|disabled|allowlist` (default `allowlist`).
+- Group policy: `channels.whatsapp.groupPolicy = open|disabled|allowlist` (default `allowlist`).
- Activation modes:
- `mention` (default): requires @mention or regex match.
- `always`: always triggers.
- `/activation mention|always` is owner-only and must be sent as a standalone message.
-- Owner = `whatsapp.allowFrom` (or self E.164 if unset).
+- Owner = `channels.whatsapp.allowFrom` (or self E.164 if unset).
- **History injection**:
- Recent messages (default 50) inserted under:
`[Chat messages since your last reply - for context]`
@@ -174,7 +178,7 @@ The wizard uses it to set your **allowlist/owner** so your own DMs are permitted
## Reply delivery (threading)
- WhatsApp Web sends standard messages (no quoted reply threading in the current gateway).
-- Reply tags are ignored on this provider.
+- Reply tags are ignored on this channel.
## Acknowledgment reactions (auto-react on receipt)
@@ -223,22 +227,22 @@ WhatsApp can automatically send emoji reactions to incoming messages immediately
- In groups with `requireMention: false` (activation: always), `group: "mentions"` will react to all messages (not just @mentions).
- Fire-and-forget: reaction failures are logged but don't prevent the bot from replying.
- Participant JID is automatically included for group reactions.
-- WhatsApp ignores `messages.ackReaction`; use `whatsapp.ackReaction` instead.
+- WhatsApp ignores `messages.ackReaction`; use `channels.whatsapp.ackReaction` instead.
## Agent tool (reactions)
- Tool: `whatsapp` with `react` action (`chatJid`, `messageId`, `emoji`, optional `remove`).
- Optional: `participant` (group sender), `fromMe` (reacting to your own message), `accountId` (multi-account).
- Reaction removal semantics: see [/tools/reactions](/tools/reactions).
-- Tool gating: `whatsapp.actions.reactions` (default: enabled).
+- Tool gating: `channels.whatsapp.actions.reactions` (default: enabled).
## Limits
-- Outbound text is chunked to `whatsapp.textChunkLimit` (default 4000).
-- Inbound media saves are capped by `whatsapp.mediaMaxMb` (default 50 MB).
+- Outbound text is chunked to `channels.whatsapp.textChunkLimit` (default 4000).
+- Inbound media saves are capped by `channels.whatsapp.mediaMaxMb` (default 50 MB).
- Outbound media items are capped by `agents.defaults.mediaMaxMb` (default 5 MB).
## Outbound send (text + media)
- Uses active web listener; error if gateway not running.
-- Text chunking: 4k max per message (configurable via `whatsapp.textChunkLimit`).
+- Text chunking: 4k max per message (configurable via `channels.whatsapp.textChunkLimit`).
- Media:
- Image/video/audio/document supported.
- Audio sent as PTT; `audio/ogg` => `audio/ogg; codecs=opus`.
@@ -258,7 +262,7 @@ WhatsApp can automatically send emoji reactions to incoming messages immediately
- **Gateway heartbeat** logs connection health (`web.heartbeatSeconds`, default 60s).
- **Agent heartbeat** is global (`agents.defaults.heartbeat.*`) and runs in the main session.
- Uses the configured heartbeat prompt (default: `Read HEARTBEAT.md if exists. Consider outstanding tasks. Checkup sometimes on your human during (user local) day time.`) + `HEARTBEAT_OK` skip behavior.
- - Delivery defaults to the last used provider (or configured target).
+ - Delivery defaults to the last used channel (or configured target).
## Reconnect behavior
- Backoff policy: `web.reconnect`:
@@ -267,22 +271,22 @@ WhatsApp can automatically send emoji reactions to incoming messages immediately
- Logged-out => stop and require re-link.
## Config quick map
-- `whatsapp.dmPolicy` (DM policy: pairing/allowlist/open/disabled).
-- `whatsapp.selfChatMode` (same-phone setup; bot uses your personal WhatsApp number).
-- `whatsapp.allowFrom` (DM allowlist).
-- `whatsapp.mediaMaxMb` (inbound media save cap).
-- `whatsapp.ackReaction` (auto-reaction on message receipt: `{emoji, direct, group}`).
-- `whatsapp.accounts..*` (per-account settings + optional `authDir`).
-- `whatsapp.accounts..mediaMaxMb` (per-account inbound media cap).
-- `whatsapp.accounts..ackReaction` (per-account ack reaction override).
-- `whatsapp.groupAllowFrom` (group sender allowlist).
-- `whatsapp.groupPolicy` (group policy).
-- `whatsapp.historyLimit` / `whatsapp.accounts..historyLimit` (group history context; `0` disables).
-- `whatsapp.groups` (group allowlist + mention gating defaults; use `"*"` to allow all)
-- `whatsapp.actions.reactions` (gate WhatsApp tool reactions).
+- `channels.whatsapp.dmPolicy` (DM policy: pairing/allowlist/open/disabled).
+- `channels.whatsapp.selfChatMode` (same-phone setup; bot uses your personal WhatsApp number).
+- `channels.whatsapp.allowFrom` (DM allowlist).
+- `channels.whatsapp.mediaMaxMb` (inbound media save cap).
+- `channels.whatsapp.ackReaction` (auto-reaction on message receipt: `{emoji, direct, group}`).
+- `channels.whatsapp.accounts..*` (per-account settings + optional `authDir`).
+- `channels.whatsapp.accounts..mediaMaxMb` (per-account inbound media cap).
+- `channels.whatsapp.accounts..ackReaction` (per-account ack reaction override).
+- `channels.whatsapp.groupAllowFrom` (group sender allowlist).
+- `channels.whatsapp.groupPolicy` (group policy).
+- `channels.whatsapp.historyLimit` / `channels.whatsapp.accounts..historyLimit` (group history context; `0` disables).
+- `channels.whatsapp.groups` (group allowlist + mention gating defaults; use `"*"` to allow all)
+- `channels.whatsapp.actions.reactions` (gate WhatsApp tool reactions).
- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`)
- `messages.groupChat.historyLimit`
-- `whatsapp.messagePrefix` (inbound prefix; per-account: `whatsapp.accounts..messagePrefix`; deprecated: `messages.messagePrefix`)
+- `channels.whatsapp.messagePrefix` (inbound prefix; per-account: `channels.whatsapp.accounts..messagePrefix`; deprecated: `messages.messagePrefix`)
- `messages.responsePrefix` (outbound prefix)
- `agents.defaults.mediaMaxMb`
- `agents.defaults.heartbeat.every`
@@ -290,7 +294,7 @@ WhatsApp can automatically send emoji reactions to incoming messages immediately
- `agents.defaults.heartbeat.target`
- `agents.defaults.heartbeat.to`
- `session.*` (scope, idle, store, mainKey)
-- `web.enabled` (disable provider startup when false)
+- `web.enabled` (disable channel startup when false)
- `web.heartbeatSeconds`
- `web.reconnect.*`
@@ -302,12 +306,12 @@ WhatsApp can automatically send emoji reactions to incoming messages immediately
## Troubleshooting (quick)
**Not linked / QR login required**
-- Symptom: `providers status` shows `linked: false` or warns “Not linked”.
-- Fix: run `clawdbot providers login` on the gateway host and scan the QR (WhatsApp → Settings → Linked Devices).
+- Symptom: `channels status` shows `linked: false` or warns “Not linked”.
+- Fix: run `clawdbot channels login` on the gateway host and scan the QR (WhatsApp → Settings → Linked Devices).
**Linked but disconnected / reconnect loop**
-- Symptom: `providers status` shows `running, disconnected` or warns “Linked but disconnected”.
-- Fix: `clawdbot doctor` (or restart the gateway). If it persists, relink via `providers login` and inspect `clawdbot logs --follow`.
+- Symptom: `channels status` shows `running, disconnected` or warns “Linked but disconnected”.
+- Fix: `clawdbot doctor` (or restart the gateway). If it persists, relink via `channels login` and inspect `clawdbot logs --follow`.
**Bun runtime**
- Bun is **not recommended**. WhatsApp (Baileys) and Telegram are unreliable on Bun.
diff --git a/docs/cli/index.md b/docs/cli/index.md
index 24ffe0f3e..63c92bc4b 100644
--- a/docs/cli/index.md
+++ b/docs/cli/index.md
@@ -51,7 +51,7 @@ clawdbot [--dev] [--profile ]
reset
uninstall
update
- providers
+ channels
list
status
logs
@@ -257,8 +257,8 @@ Options:
- `--tailscale-reset-on-exit`
- `--install-daemon`
- `--no-install-daemon` (alias: `--skip-daemon`)
-- `--daemon-runtime ` (bun not recommended for WhatsApp/Telegram)
-- `--skip-providers`
+- `--daemon-runtime `
+- `--skip-channels`
- `--skip-skills`
- `--skip-health`
- `--skip-ui`
@@ -266,7 +266,7 @@ Options:
- `--json`
### `configure` / `config`
-Interactive configuration wizard (models, providers, skills, gateway).
+Interactive configuration wizard (models, channels, skills, gateway).
### `doctor`
Health checks + quick fixes (config + gateway + legacy services).
@@ -277,41 +277,41 @@ Options:
- `--non-interactive`: skip prompts; apply safe migrations only.
- `--deep`: scan system services for extra gateway installs.
-## Provider helpers
+## Channel helpers
-### `providers`
-Manage chat provider accounts (WhatsApp/Telegram/Discord/Slack/Signal/iMessage/MS Teams).
+### `channels`
+Manage chat channel accounts (WhatsApp/Telegram/Discord/Slack/Signal/iMessage/MS Teams).
Subcommands:
-- `providers list`: show configured chat providers and auth profiles (Claude Code + Codex CLI OAuth sync included).
-- `providers status`: check gateway reachability and provider health (`--probe` runs extra checks; use `clawdbot health` or `clawdbot status --deep` for gateway health probes).
-- Tip: `providers status` prints warnings with suggested fixes when it can detect common misconfigurations (then points you to `clawdbot doctor`).
-- `providers logs`: show recent provider logs from the gateway log file.
-- `providers add`: wizard-style setup when no flags are passed; flags switch to non-interactive mode.
-- `providers remove`: disable by default; pass `--delete` to remove config entries without prompts.
-- `providers login`: interactive provider login (WhatsApp Web only).
-- `providers logout`: log out of a provider session (if supported).
+- `channels list`: show configured channels and auth profiles (Claude Code + Codex CLI OAuth sync included).
+- `channels status`: check gateway reachability and channel health (`--probe` runs extra checks; use `clawdbot health` or `clawdbot status --deep` for gateway health probes).
+- Tip: `channels status` prints warnings with suggested fixes when it can detect common misconfigurations (then points you to `clawdbot doctor`).
+- `channels logs`: show recent channel logs from the gateway log file.
+- `channels add`: wizard-style setup when no flags are passed; flags switch to non-interactive mode.
+- `channels remove`: disable by default; pass `--delete` to remove config entries without prompts.
+- `channels login`: interactive channel login (WhatsApp Web only).
+- `channels logout`: log out of a channel session (if supported).
Common options:
-- `--provider `: `whatsapp|telegram|discord|slack|signal|imessage|msteams`
-- `--account `: provider account id (default `default`)
+- `--channel `: `whatsapp|telegram|discord|slack|signal|imessage|msteams`
+- `--account `: channel account id (default `default`)
- `--name