> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stateset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Messaging Orchestrator

> Run every messaging channel from one config — shared middleware, session persistence, and event routing.

# Messaging Orchestrator

One config file runs every channel. Each gateway starts with shared middleware and routes into the
same agent and session layer, so a customer gets the same agent whether they arrive on WhatsApp or
Slack.

```bash theme={null}
stateset-channels --config examples/channels.yaml
```

## Shape of the config

```yaml theme={null}
# Applied to every channel
shared:
  dbPath: ./store.db
  allowApply: false          # read-only until you mean it
  model: claude-sonnet-4-20250514
  maxTurns: 10
  verbose: false

persistSessions: true        # SQLite-backed conversation state
```

<Warning>
  `allowApply: false` is the default and should stay false until you've watched the agent on real
  traffic. It's the same read-only-by-default posture as the
  [CLI safety model](/stateset-icommerce/stateset-icommerce-cli-safety) — a channel agent that can't
  write can't get a write wrong.
</Warning>

## Middleware

Applied across all channels, so you configure protection once rather than per gateway:

```yaml theme={null}
middleware:
  logger: true
  rateLimiter:
    maxPerMinute: 20
    maxPerHour: 200
  contentFilter:
    wordlist: [spam, scam]
    action: warn             # 'block' or 'warn'
  languageDetect: false
```

<Note>
  The rate limiter is per-channel protection against a spam flood, and also a cost control — every
  inbound message that reaches the agent costs model tokens. `maxPerHour` is the ceiling on what a
  single abusive sender can spend.
</Note>

## Notification routing

Route engine events outward to the channels that should see them:

```yaml theme={null}
notifications:
  routes:
    order.shipped:
      - channel: slack
        target: "#orders"
      - channel: telegram
        target: "-1001234567890"
    inventory.low:
      - channel: slack
        target: "#ops"
    approvals:request:created:
      - channel: slack
        target: "#approvals"
    "*":                     # wildcard catch-all
      - channel: slack
        target: "#all-alerts"
```

One event can fan out to several channels. The `"*"` route catches everything not matched above —
useful as a safety net while you learn which events matter.

<Tip>
  Route `approvals:request:created` somewhere a human actually watches. That event is the
  [review gate](/stateset-response/responsecx-workflow-studio) asking for a decision — if nobody sees
  it, the action waits indefinitely.
</Tip>

## Channels

```yaml theme={null}
channels:
  telegram:
    token: ${TELEGRAM_BOT_TOKEN}
    allowList: []                  # empty = allow all users
    # allowList: ["12345678"]      # or restrict to specific IDs

  discord:
    token: ${DISCORD_BOT_TOKEN}
    channelIds: ["1234567890123456"]

  slack:
    botToken: ${SLACK_BOT_TOKEN}
    appToken: ${SLACK_APP_TOKEN}
    signingSecret: ${SLACK_SIGNING_SECRET}

  whatsapp:
    phoneNumberId: ${WHATSAPP_PHONE_NUMBER_ID}
    accessToken: ${WHATSAPP_ACCESS_TOKEN}
    verifyToken: ${WHATSAPP_VERIFY_TOKEN}
    webhookPort: 3100

  # signal:
  #   signalCliPath: /usr/local/bin/signal-cli
  #   account: "+14155551234"

  # google-chat:
  #   projectId: ${GOOGLE_CHAT_PROJECT_ID}
  #   subscriptionName: projects/my-project/subscriptions/my-sub
```

Supported: **WhatsApp, Telegram, Slack, Discord, Signal, Google Chat**.

<Warning>
  An empty `allowList` on Telegram means **anyone** who finds the bot can talk to it. Set it while
  testing, and rely on the rate limiter and content filter once it's open.
</Warning>

Secrets come from environment variables via `${VAR}` interpolation — keep them out of the YAML.

## Where messages go

All channels feed the same session layer, so conversation state persists per user across turns and,
with `persistSessions: true`, across restarts. That's what lets a handoff work: a conversation
escalated to a human keeps its history.

## Related

* [Messaging Channels](/stateset-icommerce/stateset-icommerce-messaging-channels)
* [WhatsApp Integration](/stateset-icommerce/stateset-icommerce-whatsapp-integration)
* [CLI Safety Model](/stateset-icommerce/stateset-icommerce-cli-safety)
