Signal (signal-cli)
Status: external CLI integration. Gateway talks to signal-cli over HTTP JSON-RPC + SSE.
Quick setup (beginner)
- Use a separate Signal number for the bot (recommended).
- Install signal-cli (Java required).
- Link the bot device and start the daemon:
- signal-cli link -n "OpenClaw"
- Configure OpenClaw and start the gateway.
Minimal config:
{
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:<agentId>:signal:group:<groupId>).
Config writes
By default, Signal is allowed to write config updates triggered by /config set|unset (requires commands.config: true).
Disable with:
{
channels: { signal: { configWrites: false } }
}
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)
- Install signal-cli (Java required).
- Link a bot account:
- signal-cli link -n "OpenClaw" then scan the QR in Signal.
- Configure Signal and start the gateway.
Example:
{
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 for the shared pattern.
External daemon mode (httpUrl)
If you want to manage signal-cli yourself (slow JVM cold starts, container init, or shared CPUs), run the daemon separately and point OpenClaw at it:
{
channels: {
signal: {
httpUrl: "http://127.0.0.1:8080",
autoStart: false
}
}
}
This skips auto-spawn and the startup wait inside OpenClaw. For slow starts when auto-spawning, set channels.signal.startupTimeoutMs.
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:
- openclaw pairing list signal
- openclaw pairing approve signal <CODE>
- Pairing is the default token exchange for Signal DMs. Details: Pairing
- UUID-only senders (from sourceUuid) are stored as uuid:<id> 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).
- Optional newline chunking: set channels.signal.chunkMode="newline" to split on blank lines (paragraph boundaries) before length chunking.
- 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).
Typing + read receipts
- Typing indicators: OpenClaw sends typing signals via signal-cli sendTyping and refreshes them while a reply is running.
- Read receipts: when channels.signal.sendReadReceipts is true, OpenClaw forwards read receipts for allowed DMs.
- Signal-cli does not expose read receipts for groups.
Reactions (message tool)
- Use message action=react with channel=signal.
- Targets: sender E.164 or UUID (use uuid:<id> from pairing output; bare UUID works too).
- messageId is the Signal timestamp for the message youβre reacting to.
- Group reactions require targetAuthor or targetAuthorUuid.
Examples:
message action=react channel=signal target=uuid:123e4567-e89b-12d3-a456-426614174000 messageId=1737630212345 emoji=π₯
message action=react channel=signal target=+15551234567 messageId=1737630212345 emoji=π₯ remove=true
message action=react channel=signal target=signal:group:<groupId> targetAuthor=uuid:<sender-uuid> messageId=1737630212345 emoji=β
Config:
- channels.signal.actions.reactions: enable/disable reaction actions (default true).
- channels.signal.reactionLevel: off | ack | minimal | extensive.
- off/ack disables agent reactions (message tool react will error).
- minimal/extensive enables agent reactions and sets the guidance level.
- Per-account overrides: channels.signal.accounts.<id>.actions.reactions, channels.signal.accounts.<id>.reactionLevel.
Delivery targets (CLI/cron)
- DMs: signal:+15551234567 (or plain E.164).
- UUID DMs: uuid:<id> (or bare UUID).
- Groups: signal:group:<groupId>.
- Usernames: username:<name> (if supported by your Signal account).
Configuration reference (Signal)
Full configuration: 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.startupTimeoutMs: startup wait timeout in ms (cap 120000).
- 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:<id>). open requires "*". Signal has no usernames; use phone/UUID ids.
- 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.dmHistoryLimit: DM history limit in user turns. Per-user overrides: channels.signal.dms["<phone_or_uuid>"].historyLimit.
- channels.signal.textChunkLimit: outbound chunk size (chars).
- channels.signal.chunkMode: length (default) or newline to split on blank lines (paragraph boundaries) before length chunking.
- 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.