Channel Setup

This guide explains how to configure inbound/outbound messaging for Signal, Telegram, Discord, and Slack. It focuses on Carapace service wiring and the minimum external setup needed to make each channel usable.

For step-by-step channel onboarding recipes, see the Cookbook. For real-world validation criteria and evidence capture, see Channel Smoke Validation.

All examples assume carapace.json5 (see config.example.json5) and the default service HTTP port 18789. Adjust paths/ports for your deployment.

Common Notes

Signal (signal-cli-rest-api)

Signal uses a polling loop against the local signal-cli-rest-api container. Inbound messages are delivered by polling GET /v1/receive/{number}.

  1. Start signal-cli-rest-api:
docker run -d -p 8080:8080 -v $HOME/.local/share/signal-api:/home/.local/share/signal-cli \
  -e MODE=native bbernhard/signal-cli-rest-api
  1. Configure Carapace:
{
  "signal": {
    "baseUrl": "http://localhost:8080",
    "phoneNumber": "+15551234567"
  },
  "channels": {
    "signal": {
      "features": {
        "typing": {
          "enabled": true
        },
        "readReceipts": {
          "enabled": true
        }
      }
    }
  }
}

For non-loopback Signal deployments, set signal.baseUrl to https://.... Carapace rejects non-HTTPS non-loopback Signal URLs.

When the sender has phone-number privacy enabled, Signal delivers a sourceUuid instead of a sourceNumber. Carapace falls back to the UUID as the sender identifier in that case.

When channels.signal.features.typing.enabled is true, Carapace refreshes the Signal typing indicator while the assistant is generating a reply and clears it before outbound delivery. When channels.signal.features.readReceipts.enabled is true, Carapace polls Signal with send_read_receipts=false and only sends a read receipt after the inbound message is durably appended to Carapace's session/history store. This happens before any LLM response is generated or delivered. If the append fails, Carapace leaves the message unread. Unsupported Signal messages that Carapace does not ingest today, including group messages and non-text messages, also remain unread while this feature is enabled. When the feature is disabled, Signal keeps its normal auto-read-receipt behavior.

Telegram (Bot API + Webhook or Polling)

Telegram uses the Bot API for outbound delivery. Inbound can run in either mode:

  1. Create a Telegram bot token (via BotFather).
  2. Configure Carapace:
{
  "telegram": {
    "botToken": "${TELEGRAM_BOT_TOKEN}",
    // webhookSecret: "${TELEGRAM_WEBHOOK_SECRET}" // optional; enables webhook mode
  }
}
  1. Optional webhook setup (if using webhook mode):
https://YOUR_HOST/channels/telegram/webhook

Inbound webhook requests are rejected if the configured secret is missing or does not match.

Slack (Web API + Events API)

Slack uses the Web API for outbound delivery and the Events API for inbound messages.

  1. Create a Slack app, install it to your workspace, and obtain a bot token (xoxb-...).
  2. Enable Events API and set the request URL to:
https://YOUR_HOST/channels/slack/events
  1. Configure the Slack signing secret in Carapace:
{
  "slack": {
    "botToken": "${SLACK_BOT_TOKEN}",
    "signingSecret": "${SLACK_SIGNING_SECRET}"
  }
}

Carapace validates X-Slack-Request-Timestamp and X-Slack-Signature. Slack’s url_verification handshake is supported.

Discord (REST + Gateway)

Discord uses the REST API for outbound delivery and the Discord Gateway WebSocket for inbound messages.

  1. Create a Discord application and bot token.
  2. Enable the Message Content Intent if you want access to full message content in guilds.
  3. Configure Carapace:
{
  "discord": {
    "botToken": "${DISCORD_BOT_TOKEN}",
    "gatewayEnabled": true,
    "gatewayIntents": 37377 // includes MESSAGE_CONTENT by default
  }
}

Carapace connects to Discord and dispatches MESSAGE_CREATE events into the agent pipeline.

Verify Channel Wiring

After configuring a channel, validate from another terminal while Carapace is running:

cara verify --outcome discord --port 18789 --discord-to "<channel_id>"
cara verify --outcome telegram --port 18789 --telegram-to "<chat_id>"

Notes:

Environment Variables

All channel config can be supplied via environment variables:

Inbound Session Routing

Inbound messages create (or reuse) a scoped session key based on channel + sender + peer ID. Responses are delivered back through the channel pipeline.