- The fastest path uses signal-cli in JSON-RPC mode — works on Linux and macOS without D-Bus configuration
- Channel YAML uses
type: signalwithsignal_cli_urlpointing to the local JSON-RPC endpoint - Docker deployments need a persistent volume for the signal-cli data directory or you lose registration
- Test with
signal-cli sendfrom the CLI before troubleshooting OpenClaw — isolate each layer - Keep the account active with a monthly automated ping — Signal deregisters idle accounts after several months
Most Signal integration guides bury you in theory before showing a single command. This one runs the opposite direction: here's what to run, here's what the config looks like, here's how you verify it works. You can revisit the theory after your agent is responding to Signal messages.
How the OpenClaw Signal Integration Works
The integration has three components: signal-cli (the Signal protocol client), the OpenClaw Signal channel adapter (which talks to signal-cli), and your agent configuration (which defines how the agent responds). They form a chain.
A Signal message arrives at the Signal servers. Signal-cli, running as a linked device, fetches it and exposes it to the OpenClaw channel adapter via JSON-RPC. The adapter passes the message to your configured agent, which processes it and sends a response back through the same chain.
The key insight: the Signal encryption layer happens between Signal servers and signal-cli. OpenClaw sits entirely on the decrypted side of that boundary — on your infrastructure, under your control. No message content touches any external service except your configured LLM API.
Signal-cli supports two daemon interfaces: D-Bus (Linux-only, requires dbus session) and JSON-RPC (cross-platform, HTTP-based). Use JSON-RPC unless you have a specific reason not to. It's easier to configure, works in Docker, and exposes a clean interface that OpenClaw handles well.
Setting Up signal-cli in Daemon Mode
First, confirm Java 17+ is installed. Then get signal-cli running as a daemon. This is the step most people rush and then spend an hour debugging.
# Confirm Java version (must be 17+)
java --version
# Create a data directory for signal-cli
mkdir -p ~/.local/share/signal-cli
# Register and verify your dedicated phone number
signal-cli -u +15551234567 register
signal-cli -u +15551234567 verify 123456
# Start the daemon in JSON-RPC mode
signal-cli -u +15551234567 daemon \
--json-rpc \
--http-port 8180
The --http-port 8180 flag starts the JSON-RPC HTTP server. OpenClaw will connect to this. Keep port 8180 (or whatever port you choose) away from public internet access — it has no authentication built in.
We'll get to the systemd service file in a moment — but first confirm the daemon is responding before wiring up OpenClaw. Send a test message from a different terminal.
# Test the daemon is responsive (from a second terminal)
curl -s http://localhost:8180/api/v1/about | python3 -m json.tool
You should see a JSON response with version and capabilities. If you get a connection refused error, the daemon isn't running or is listening on a different port. Fix that before touching OpenClaw.
Create a systemd Service for signal-cli
You need signal-cli to survive reboots and auto-restart on crashes. Create a systemd service:
[Unit]
Description=signal-cli daemon
After=network.target
[Service]
Type=simple
User=yourusername
ExecStart=/usr/local/bin/signal-cli -u +15551234567 daemon --json-rpc --http-port 8180
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Save this to /etc/systemd/system/signal-cli.service, then enable and start it:
sudo systemctl daemon-reload
sudo systemctl enable signal-cli
sudo systemctl start signal-cli
sudo systemctl status signal-cli
OpenClaw Channel YAML Configuration
With signal-cli running and verified, add the channel to your agent's YAML configuration file. The minimal working config looks like this:
channels:
- type: signal
name: signal-agent
phone_number: "+15551234567"
signal_cli_url: "http://localhost:8180"
polling_interval_ms: 1000
system_prompt: |
You are a private AI assistant accessible via Signal.
Respond concisely. Avoid markdown — Signal renders it as raw text.
Keep responses under 300 words unless the user asks for detail.
model: gpt-4o-mini
max_tokens: 800
The polling_interval_ms controls how often OpenClaw asks signal-cli for new messages. 1000ms (one second) is a reasonable balance between responsiveness and resource usage. For lower-traffic personal agents, 3000ms is fine.
The signal-cli JSON-RPC HTTP interface has no authentication. Anyone who can reach that port can send and receive Signal messages as your registered number. Bind it to localhost only (the default) and access it only from OpenClaw running on the same machine or in the same Docker network.
Docker Deployment
Running signal-cli and OpenClaw in Docker requires one critical detail: the signal-cli data directory must be on a persistent volume. Lose that directory and you lose your Signal registration, requiring a full re-register with another SMS verification code.
version: '3.8'
services:
signal-cli:
image: bbernhard/signal-cli-rest-api:latest
environment:
- MODE=json-rpc
volumes:
- signal-cli-data:/home/.local/share/signal-cli
ports:
- "127.0.0.1:8180:8080"
openclaw-agent:
image: openclaw/agent:latest
depends_on:
- signal-cli
volumes:
- ./agent-config.yaml:/config/agent.yaml
environment:
- OPENCLAW_CONFIG=/config/agent.yaml
volumes:
signal-cli-data:
Use the bbernhard/signal-cli-rest-api image — it packages signal-cli with a REST wrapper that exposes a clean JSON-RPC interface. Set signal_cli_url in your OpenClaw config to http://signal-cli:8080 when using Docker Compose networking.
Testing the Integration End to End
Don't start the full OpenClaw stack and then wonder why it's not working. Test each layer separately.
- Test signal-cli registration — send a message to the registered number from your personal Signal app. Check signal-cli logs to confirm it was received.
- Test the JSON-RPC endpoint — use curl to query the signal-cli HTTP API and confirm it returns message data.
- Test OpenClaw channel load — start OpenClaw and check the gateway logs. The Signal channel should appear as registered.
- Test agent response — send a message from Signal. Check OpenClaw logs for the inbound message event, LLM API call, and outbound response. Then check your Signal app for the reply.
Here's where most people stop. They test step 4 first, get nothing, and spend an hour debugging OpenClaw when the real issue is signal-cli not receiving the message. Always isolate each layer.
Common Integration Mistakes
- Using signal-cli without a persistent data directory in Docker — container restarts wipe your registration. Always mount a named volume.
- Pointing
signal_cli_urlat the wrong port — double-check the port signal-cli is actually listening on before debugging OpenClaw. - Not testing signal-cli independently before adding OpenClaw — isolate each layer. If signal-cli can't receive messages, OpenClaw can't fix that.
- Setting polling_interval_ms too low — 100ms polling creates unnecessary CPU load. 1000ms is plenty for conversational agents.
- Ignoring account activity requirements — a Signal account that receives no messages for months can expire. Set a cron job to send a test message monthly.
Frequently Asked Questions
How long does the OpenClaw Signal integration take to set up?
Most setups complete in 20–40 minutes. The main variable is signal-cli registration — fast SMS delivery means a fast start. Java version mismatches or socket path issues can add another 30 minutes of troubleshooting if you encounter them.
What signal-cli mode should I use — D-Bus or JSON-RPC?
JSON-RPC mode for most setups. It works on Linux and macOS, requires no D-Bus configuration, and exposes a clean HTTP interface. D-Bus mode is Linux-only and adds complexity without meaningful performance advantages for conversational agent workloads.
Can the integration handle file and image attachments?
Yes. Signal attachments pass through signal-cli to OpenClaw as file references. Vision-capable models can analyze images directly. Other models receive a file path reference they can acknowledge but not visually process — configure your system prompt accordingly.
Does the Signal integration work inside Docker?
Yes, with the critical caveat that the signal-cli data directory must be on a persistent named volume. Losing it means re-registering the phone number from scratch. Use Docker Compose networking to connect containers without exposing the signal-cli port to the host network.
How do I test the integration without sending real Signal messages?
Use signal-cli send from the command line to send a test message to the registered number, then check OpenClaw logs to confirm receipt and processing. This validates the full pipeline without needing a second Signal account or device.
What causes 'Registration expired' errors in signal-cli?
Signal deregisters accounts inactive for extended periods — typically several months. Keep the account active by sending a periodic test message via cron. If registration expires, you must re-register the number from scratch with a new SMS verification code.
A. Larsen specializes in connecting OpenClaw to privacy-preserving communication channels. Has deployed Signal-based agent integrations for legal tech firms, healthcare adjacent workflows, and private research teams — and has debugged every signal-cli failure mode documented here personally.