channels

Telegram today. The rest behind the same trait.

openclaw-channels exposes the Channel, ChannelInbound, and ChannelOutbound traits. A full Telegram Bot API adapter ships today. Discord, Slack, Signal, Matrix, and WhatsApp are tracked roadmap items; each is a single-file PR.

Telegram shipped

Full Bot API adapter with attachments, allowlist-based access control, and rule-based agent routing.

  • Long polling + webhook
  • Photo / document / voice attachments
  • Per-channel allowlist
  • Routes to multiple agents by rule
Discord planned

Slash commands, threaded conversations, attachments.

  • Roadmap target
  • Trait-based, single-PR add
Slack planned

App Mention, DM, slash commands, threaded replies.

  • Roadmap target
  • Bolt-equivalent surface
Signal planned

Privacy-first channel via signal-cli or signald.

  • Roadmap target
Matrix planned

Federated open-protocol adapter.

  • Roadmap target
  • End-to-end encrypted rooms
WhatsApp planned

WhatsApp Business API, with template-message support.

  • Roadmap target

The Channel trait

rust
pub trait Channel: Send + Sync {
    fn id(&self) -> ChannelId;
    fn name(&self) -> &str;
}

#[async_trait]
pub trait ChannelInbound: Channel {
    async fn run(&self, sink: mpsc::Sender<InboundEvent>) -> Result<()>;
}

#[async_trait]
pub trait ChannelOutbound: Channel {
    async fn send(&self, to: PeerId, message: OutboundMessage) -> Result<MessageId>;
}

Allowlist + routing

Before an inbound event reaches an agent, it passes through an allowlist and a router. Default allowlist is deny-all. Default router has no rules — you have to opt in.

rust
let allowlist = Allowlist::new()
    .allow(AllowRule::PeerId("123456789".into()))
    .allow(AllowRule::GroupId("-1001234567890".into()));

let router = Router::new()
    .add(100, RouteMatcher::Keyword("/code".into()), AgentId("code".into()))
    .add(0,   RouteMatcher::Always,                  AgentId("default".into()));

Telegram adapter

Full Bot API client built on reqwest:

  • Long-polling (webhook support on the roadmap).
  • Photo / document / voice / video attachments, normalised into the runtime's Attachment shape.
  • Bot token stored as an ApiKey with redaction guarantees.
  • Per-channel allowlist and routing rules.
openclaw.json
{
  "channels": {
    "telegram": {
      "enabled": true,
      "bot_token": "{{ TELEGRAM_BOT_TOKEN }}",
      "allowlist": [
        { "kind": "PeerId",  "value": "123456789" }
      ],
      "routing": [
        { "priority": 100, "match": { "kind": "Keyword", "value": "/code" }, "target": "code-agent" },
        { "priority": 0,   "match": { "kind": "Always" },                    "target": "default" }
      ]
    }
  }
}

{{ TELEGRAM_BOT_TOKEN }} is resolved from the credential store at load. The plaintext never sits in the config file.

Adding a new adapter

The Telegram adapter is the reference. To add Discord, Slack, Signal, Matrix, or WhatsApp:

  1. Implement Channel + ChannelInbound + ChannelOutbound in a new module under crates/openclaw-channels/.
  2. Register it in ChannelRegistry::register().
  3. Add a config schema entry.
  4. Write a smoke test.

Open a PR. We'll review.

The deep dive

For the full walkthrough — Bot API specifics, attachment handling, outbound, configuration — see the Telegram-as-channel article.

contribute

Want Discord or Slack next?

The trait is stable. The Telegram adapter is the reference. PRs welcome — open one and we'll review.