Skip to main content
Connect your AI agent to Signal — end-to-end encrypted, linked through a local signal-cli-rest-api bridge, no cloud bot token needed.
The user messages you on Signal; the bridge relays the chat to the agent and the agent replies in the same thread.

Quick Start

1

Run the bridge

Signal links a device through the signal-cli-rest-api bridge. Start it with Docker in native mode.
2

Link your device

Open the bridge link URL, then scan the QR from Signal → Settings → Linked Devices → Link New Device.
3

Start the bot

Point the bot at your linked account and the bridge.
Message your linked Signal number and the agent replies.
No token needed — Signal links a device via the local bridge. The token constructor argument exists only for adapter-wiring parity and is unused.

How It Works

Signal is poll-based: the bot fetches inbound envelopes from the bridge, routes text to the agent, and sends the reply back through the bridge. The adapter talks HTTP/JSON to these bridge endpoints: Inbound liveness. A delivered envelope refreshes the Signal channel’s last_activity for the shared Channel Supervision health monitor. Successfully-empty polls do not refresh it, so a silently half-open bridge (returns [] forever without raising) trips stale-socket after stale_after seconds and the supervisor reconnects — instead of the bot going quietly deaf.

Configuration

Signal needs an account and a bridge URL. Configure it with YAML, environment variables, or the Python SDK.

Config keys

Constructor parameters


Capabilities & Limits

Signal declares honest capabilities so shared engines degrade correctly.

Built-in Commands

Every Signal channel ships these commands out of the box. Register custom commands:

DM Allowlist

Restrict who can DM the bot with allowed_users — phone numbers are normalised to digits so formatting never blocks a match.
Groups pass through the allowlist — group membership is the gate there, not the DM list.

What’s Not Supported Yet

This release ships text messaging. The following are intentionally not wired:

Troubleshooting

The account is empty. Set it via the constructor, channels.signal.account, or the SIGNAL_ACCOUNT environment variable before calling start().
The Signal adapter uses aiohttp (lazy-imported). Install it:
Confirm the bridge is linked and reachable. Check GET /v1/accounts returns your number, and lower poll_interval (minimum 0.2s) for faster pickup. Verify the bridge runs in native or json-rpc mode so /v1/receive returns envelopes.
The signal-cli-rest-api bridge can go silently half-open — the HTTP call succeeds but the bridge returns [] forever. The gateway health monitor treats time since the last delivered envelope as the liveness signal (empty polls don’t count), so the channel will trip stale-socket after stale_after seconds (default 120s) and the supervisor will reconnect automatically. Confirm via GET /health that last_activity for the Signal channel is advancing on real inbound messages, and consider lowering stale_after on very-chatty accounts or raising it on quiet ones. See Channel Supervision → Proactive Health Monitoring.
probe() calls GET /v1/accounts. A non-200 means the bridge is down or the URL is wrong. Ensure bridge_url matches the Docker port mapping (default http://localhost:8080).

Best Practices

The signal-cli-rest-api bridge holds your linked device. Run it on localhost or a private network — never expose port 8080 to the public internet.
Set allowed_users so only trusted numbers can start a conversation. Numbers normalise to digits, so any formatting works.
poll_interval trades latency for load. Keep the default 1.0s for most bots; lower it toward 0.2s for snappier replies, raise it to reduce bridge traffic.
Signal has no message editing and this release is text-only. Design replies as self-contained plain-text messages rather than relying on edits or rich media.

Standalone scheduled delivery (no gateway)

You do not need a running Signal Bot process to deliver a scheduled brief to Signal. Point SIGNAL_ACCOUNT at your linked sender number (and, if the bridge is not on http://localhost:8080, set SIGNAL_BRIDGE_URL). From OS cron / CI, praisonai schedule tick will POST to {SIGNAL_BRIDGE_URL}/v2/send using an explicit deliver: signal:<recipient> target or SIGNAL_HOME_CHANNEL. The bridge must be reachable from wherever the tick fires. See Out-of-process delivery.

WhatsApp Bot

The closest sibling — linked-device / local-bridge model

Channels Gateway

Run Signal alongside Telegram, Discord, Slack, and more

Messaging Bots

All supported messaging platforms

Bot Gateway

Multi-channel gateway architecture
Self-healing gateway channels — how Signal integrates with the health monitor