# Signal CLI REST API Signal CLI REST API is a Dockerized REST API wrapper around signal-cli, enabling programmatic access to Signal Messenger functionality. The project provides a complete HTTP interface for sending and receiving Signal messages, managing contacts and groups, handling attachments, and controlling device registration—all without requiring the official Signal mobile app for day-to-day operations. Built with Go and the Gin web framework, the API supports three execution modes: normal (standard JVM-based signal-cli), native (GraalVM-compiled binary for faster startup), and json-rpc (persistent daemon for maximum performance). The service exposes comprehensive endpoints for message operations, group management, profile updates, device linking, typing indicators, reactions, receipts, and more. It's designed for integration into home automation systems, notification services, chatbots, and custom Signal-based applications. ## API Endpoints ### Register Phone Number Register a new phone number with the Signal network, optionally using voice verification or captcha. ```bash # SMS verification (default) curl -X POST -H "Content-Type: application/json" \ 'http://localhost:8080/v1/register/+431212131491291' # Voice verification curl -X POST -H "Content-Type: application/json" \ --data '{"use_voice": true}' \ 'http://localhost:8080/v1/register/+431212131491291' # With captcha (required when Signal requests it) # 1. Visit https://signalcaptchas.org/registration/generate.html # 2. Extract captcha value from browser console # 3. Use in registration: curl -X POST -H "Content-Type: application/json" \ -d '{"captcha":"signalcaptcha://03AGdBq24QSFj..."}' \ 'http://localhost:8080/v1/register/+431212131491291' ``` ### Verify Phone Number Verify a registered phone number using the SMS/voice verification code. ```bash curl -X POST -H "Content-Type: application/json" \ 'http://localhost:8080/v1/register/+431212131491291/verify/123-456' # With PIN (if account uses Signal PIN) curl -X POST -H "Content-Type: application/json" \ --data '{"pin": "1234"}' \ 'http://localhost:8080/v1/register/+431212131491291/verify/123-456' ``` ### Unregister Phone Number Unregister a phone number from Signal, optionally deleting the account and local data. ```bash # Unregister without deleting account or local data curl -X POST -H "Content-Type: application/json" \ 'http://localhost:8080/v1/unregister/+431212131491291' # Unregister and delete account from Signal servers curl -X POST -H "Content-Type: application/json" \ -d '{"delete_account": true}' \ 'http://localhost:8080/v1/unregister/+431212131491291' # Unregister, delete account, and delete local data curl -X POST -H "Content-Type: application/json" \ -d '{"delete_account": true, "delete_local_data": true}' \ 'http://localhost:8080/v1/unregister/+431212131491291' ``` ### Link Device via QR Code Link the API instance as a secondary device by scanning a QR code with your primary Signal app. ```bash # Generate QR code (open in browser or use curl to get PNG) curl -X GET 'http://localhost:8080/v1/qrcodelink?device_name=signal-api' # Or with wget to save QR code image wget -O qrcode.png 'http://localhost:8080/v1/qrcodelink?device_name=HomeAssistant' # Then: Open Signal app → Settings → Linked devices → + → Scan QR code ``` ### Send Message (v2) Send a text message with optional attachments, mentions, quotes, stickers, and formatting. ```bash # Simple text message curl -X POST -H "Content-Type: application/json" \ -d '{ "message": "Hello from Signal API!", "number": "+431212131491291", "recipients": ["+4354546464654", "+4912812812121"] }' \ 'http://localhost:8080/v2/send' # Response: {"timestamp":"1697123456789"} # Message with base64 attachment ATTACHMENT=$(base64 -w 0 image.jpg) curl -X POST -H "Content-Type: application/json" \ -d "{ \"message\": \"Check this out!\", \"number\": \"+431212131491291\", \"recipients\": [\"+4354546464654\"], \"base64_attachments\": [\"${ATTACHMENT}\"] }" \ 'http://localhost:8080/v2/send' # Styled text message (bold, italic, strikethrough) curl -X POST -H "Content-Type: application/json" \ -d '{ "message": "*bold text* _italic text_ ~strikethrough~ ||spoiler|| `monospace`", "number": "+431212131491291", "recipients": ["+4354546464654"], "text_mode": "styled" }' \ 'http://localhost:8080/v2/send' # Message with mentions curl -X POST -H "Content-Type: application/json" \ -d '{ "message": "Hey @John, check this out!", "number": "+431212131491291", "recipients": ["+4354546464654"], "mentions": [ {"author": "+4354546464654", "start": 4, "length": 5} ] }' \ 'http://localhost:8080/v2/send' # Quote/reply to message curl -X POST -H "Content-Type: application/json" \ -d '{ "message": "I agree!", "number": "+431212131491291", "recipients": ["+4354546464654"], "quote_timestamp": 1697123456789, "quote_author": "+4354546464654", "quote_message": "Original message text" }' \ 'http://localhost:8080/v2/send' # Send to group (get group_id from /v1/groups/{number}) curl -X POST -H "Content-Type: application/json" \ -d '{ "message": "Hello group!", "number": "+431212131491291", "recipients": ["group.ckRzaEd4VmRzNnJaASAEsasa"] }' \ 'http://localhost:8080/v2/send' # Link preview THUMB=$(base64 -w 0 thumbnail.png) curl -X POST -H "Content-Type: application/json" \ -d "{ \"message\": \"Check out https://www.example.com\", \"number\": \"+431212131491291\", \"recipients\": [\"+4354546464654\"], \"link_preview\": { \"url\": \"https://www.example.com\", \"title\": \"Example Site\", \"description\": \"An example website\", \"base64_thumbnail\": \"${THUMB}\" } }" \ 'http://localhost:8080/v2/send' # View-once image curl -X POST -H "Content-Type: application/json" \ -d "{ \"message\": \"Self-destructing image\", \"number\": \"+431212131491291\", \"recipients\": [\"+4354546464654\"], \"base64_attachments\": [\"${ATTACHMENT}\"], \"view_once\": true }" \ 'http://localhost:8080/v2/send' # Edit a previously sent message curl -X POST -H "Content-Type: application/json" \ -d '{ "message": "Updated message text", "number": "+431212131491291", "recipients": ["+4354546464654"], "edit_timestamp": 1697123456789 }' \ 'http://localhost:8080/v2/send' ``` ### Receive Messages Subscribe to incoming messages via WebSocket connection for real-time message reception. ```bash # Using curl (limited WebSocket support) curl -X GET 'http://localhost:8080/v1/receive/+431212131491291' # Using wscat (recommended for WebSocket) wscat -c ws://localhost:8080/v1/receive/+431212131491291 # Python example for message reception import websocket import json def on_message(ws, message): data = json.loads(message) print(f"From: {data['envelope']['source']}") print(f"Message: {data['envelope']['dataMessage']['message']}") if 'attachments' in data['envelope']['dataMessage']: for att in data['envelope']['dataMessage']['attachments']: print(f"Attachment: {att['id']}") ws = websocket.WebSocketApp( "ws://localhost:8080/v1/receive/+431212131491291", on_message=on_message ) ws.run_forever() ``` ### Create Group Create a new Signal group with specified members and settings. ```bash # Basic group curl -X POST -H "Content-Type: application/json" \ -d '{ "name": "My Project Team", "members": ["+4354546464654", "+4912812812121"] }' \ 'http://localhost:8080/v1/groups/+431212131491291' # Response: {"id":"group.ckRzaEd4VmRzNnJaASAEsasa"} # Group with description and permissions curl -X POST -H "Content-Type: application/json" \ -d '{ "name": "Family Group", "description": "Our family chat", "members": ["+4354546464654", "+4912812812121", "+4976543210987"], "permissions": { "add_members": "only-admins", "edit_group": "only-admins", "send_messages": "every-member" }, "group_link": "enabled-with-approval", "expiration_time": 86400 }' \ 'http://localhost:8080/v1/groups/+431212131491291' ``` ### List Groups Retrieve all groups for a registered number. ```bash # List all groups curl -X GET -H "Content-Type: application/json" \ 'http://localhost:8080/v1/groups/+431212131491291' # Response example: # [ # { # "id": "group.ckRzaEd4VmRzNnJaASAEsasa", # "internal_id": "abc123...", # "name": "Family Group", # "description": "Our family chat", # "members": ["+4354546464654", "+4912812812121"], # "admins": ["+431212131491291"], # "blocked": false, # "pending_invites": [], # "pending_requests": [], # "invite_link": "https://signal.group/..." # } # ] # Get specific group details curl -X GET -H "Content-Type: application/json" \ 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa' ``` ### Update Group Modify group settings, members, or admins. ```bash # Update group name and description curl -X PUT -H "Content-Type: application/json" \ -d '{ "name": "Updated Group Name", "description": "New description" }' \ 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa' # Add members curl -X POST -H "Content-Type: application/json" \ -d '{ "members": ["+4988877766655"] }' \ 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa/members' # Remove members curl -X DELETE -H "Content-Type: application/json" \ -d '{ "members": ["+4988877766655"] }' \ 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa/members' # Add admins curl -X POST -H "Content-Type: application/json" \ -d '{ "admins": ["+4354546464654"] }' \ 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa/admins' # Update avatar AVATAR=$(base64 -w 0 group_avatar.jpg) curl -X PUT -H "Content-Type: application/json" \ -d "{ \"base64_avatar\": \"${AVATAR}\" }" \ 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa' ``` ### Delete Group Delete a Signal group permanently. ```bash curl -X DELETE -H "Content-Type: application/json" \ 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa' ``` ### Block Group Block a Signal group to stop receiving messages from it. ```bash curl -X POST -H "Content-Type: application/json" \ 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa/block' ``` ### Join Group Join a Signal group using an invite link. ```bash curl -X POST -H "Content-Type: application/json" \ -d '{ "group_link": "https://signal.group/#..." }' \ 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa/join' ``` ### Quit Group Leave a Signal group without deleting it. ```bash curl -X POST -H "Content-Type: application/json" \ 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa/quit' ``` ### Get Group Avatar Retrieve the avatar image for a specific group. ```bash curl -X GET 'http://localhost:8080/v1/groups/+431212131491291/group.ckRzaEd4VmRzNnJaASAEsasa/avatar' \ --output group_avatar.jpg ``` ### Send Reaction React to a message with an emoji. ```bash curl -X POST -H "Content-Type: application/json" \ -d '{ "recipient": "+4354546464654", "reaction": "👍", "target_author": "+4354546464654", "timestamp": 1697123456789 }' \ 'http://localhost:8080/v1/reactions/+431212131491291' # Remove reaction curl -X DELETE -H "Content-Type: application/json" \ -d '{ "recipient": "+4354546464654", "reaction": "", "target_author": "+4354546464654", "timestamp": 1697123456789 }' \ 'http://localhost:8080/v1/reactions/+431212131491291' ``` ### Send Receipt Send read or viewed receipt for a message. ```bash # Send read receipt curl -X POST -H "Content-Type: application/json" \ -d '{ "recipient": "+4354546464654", "receipt_type": "read", "timestamp": 1697123456789 }' \ 'http://localhost:8080/v1/receipts/+431212131491291' # Send viewed receipt (for view-once messages) curl -X POST -H "Content-Type: application/json" \ -d '{ "recipient": "+4354546464654", "receipt_type": "viewed", "timestamp": 1697123456789 }' \ 'http://localhost:8080/v1/receipts/+431212131491291' ``` ### Typing Indicator Show or hide typing indicator to a recipient. ```bash # Start typing indicator curl -X PUT -H "Content-Type: application/json" \ -d '{ "recipient": "+4354546464654" }' \ 'http://localhost:8080/v1/typing-indicator/+431212131491291' # Stop typing indicator curl -X DELETE -H "Content-Type: application/json" \ -d '{ "recipient": "+4354546464654" }' \ 'http://localhost:8080/v1/typing-indicator/+431212131491291' ``` ### Update Profile Update your Signal profile name, avatar, and about text. ```bash # Update name only curl -X PUT -H "Content-Type: application/json" \ -d '{ "name": "John Doe" }' \ 'http://localhost:8080/v1/profiles/+431212131491291' # Update with avatar and about AVATAR=$(base64 -w 0 profile_pic.jpg) curl -X PUT -H "Content-Type: application/json" \ -d "{ \"name\": \"John Doe\", \"base64_avatar\": \"${AVATAR}\", \"about\": \"Signal user since 2024\" }" \ 'http://localhost:8080/v1/profiles/+431212131491291' ``` ### Manage Contacts List, update, and sync contacts. ```bash # List all contacts curl -X GET 'http://localhost:8080/v1/contacts/+431212131491291' # Response example: # [ # { # "number": "+4354546464654", # "uuid": "abc-123-def-456", # "name": "John Smith", # "profile_name": "John", # "color": "blue", # "blocked": false, # "message_expiration": "0", # "profile": { # "given_name": "John", # "about": "Engineer", # "has_avatar": true # } # } # ] # Update contact curl -X PUT -H "Content-Type: application/json" \ -d '{ "recipient": "+4354546464654", # "name": "John Smith", "expiration_in_seconds": 86400 }' \ 'http://localhost:8080/v1/contacts/+431212131491291' # Sync contacts to linked devices curl -X POST 'http://localhost:8080/v1/contacts/+431212131491291/sync' # Get specific contact by UUID curl -X GET 'http://localhost:8080/v1/contacts/+431212131491291/abc-123-def-456' # Get contact avatar curl -X GET 'http://localhost:8080/v1/contacts/+431212131491291/abc-123-def-456/avatar' \ --output contact_avatar.jpg ``` ### Search Phone Numbers Check if phone numbers are registered with Signal. ```bash # Single number curl -X GET 'http://localhost:8080/v1/search/+431212131491291?numbers=%2B4354546464654' # Multiple numbers (URL-encoded) curl -X GET 'http://localhost:8080/v1/search/+431212131491291?numbers=%2B4354546464654,%2B4912812812121' # Response: # [ # {"number": "+4354546464654", "registered": true}, # {"number": "+4912812812121", "registered": false} # ] ``` ### Manage Attachments List, serve, and delete attachments. ```bash # List all stored attachments curl -X GET 'http://localhost:8080/v1/attachments' # Response: ["attachment_id_1", "attachment_id_2"] # Download attachment curl -X GET 'http://localhost:8080/v1/attachments/attachment_id_1' \ --output downloaded_file.jpg # Delete attachment curl -X DELETE 'http://localhost:8080/v1/attachments/attachment_id_1' ``` ### Trust Identity Trust a contact's safety number for enhanced security. ```bash # Trust with safety number verification curl -X PUT -H "Content-Type: application/json" \ -d '{ "verified_safety_number": "12345 67890 12345 67890 12345 67890" }' \ 'http://localhost:8080/v1/identities/+431212131491291/trust/+4354546464654' # Trust all known keys curl -X PUT -H "Content-Type: application/json" \ -d '{ "trust_all_known_keys": true }' \ 'http://localhost:8080/v1/identities/+431212131491291/trust/+4354546464654' # List identities curl -X GET 'http://localhost:8080/v1/identities/+431212131491291' ``` ### List Registered Accounts List all registered and linked accounts on this API instance. ```bash curl -X GET 'http://localhost:8080/v1/accounts' # Response example: # ["+431212131491291", "+441234567890"] ``` ### Manage Devices List and add linked devices to your Signal account. ```bash # List linked devices curl -X GET 'http://localhost:8080/v1/devices/+431212131491291' # Response example: # { # "devices": [ # { # "id": 1, # "name": "signal-api", # "created": 1697123456789, # "last_seen": 1697234567890 # } # ] # } # Add a new device by URI (from another device's QR code) curl -X POST -H "Content-Type: application/json" \ -d '{ "uri": "sgnl://linkdevice?uuid=..." }' \ 'http://localhost:8080/v1/devices/+431212131491291' ``` ### Account Management Set username, manage PIN, update account settings. ```bash # Set username curl -X POST -H "Content-Type: application/json" \ -d '{ "username": "johndoe.42" }' \ 'http://localhost:8080/v1/accounts/+431212131491291/username' # Response: {"username": "johndoe.42", "username_link": "https://signal.me/#..."} # Remove username curl -X DELETE 'http://localhost:8080/v1/accounts/+431212131491291/username' # Set Signal PIN curl -X POST -H "Content-Type: application/json" \ -d '{ "pin": "123456" }' \ 'http://localhost:8080/v1/accounts/+431212131491291/pin' # Remove PIN curl -X DELETE 'http://localhost:8080/v1/accounts/+431212131491291/pin' # Update account settings curl -X PUT -H "Content-Type: application/json" \ -d '{ "discoverable_by_number": true, "share_number": false }' \ 'http://localhost:8080/v1/accounts/+431212131491291/settings' ``` ### Rate Limit Challenge Solve captcha to lift rate limit restrictions. ```bash # When you receive 429 error with challenge_tokens, solve captcha: # 1. Visit https://signalcaptchas.org/challenge/generate.html # 2. Get captcha value from console # 3. Submit with challenge token: curl -X POST -H "Content-Type: application/json" \ -d '{ "challenge_token": "token_from_error_response", "captcha": "signalcaptcha://03AGdBq24..." }' \ 'http://localhost:8080/v1/accounts/+431212131491291/rate-limit-challenge' ``` ### Sticker Packs List and add sticker packs. ```bash # List installed sticker packs curl -X GET 'http://localhost:8080/v1/sticker-packs/+431212131491291' # Add sticker pack (get pack_id and pack_key from https://signalstickers.org) curl -X POST -H "Content-Type: application/json" \ -d '{ "pack_id": "9a32eda01a7a28574f2eb48668ae0dc4", "pack_key": "19546e18eba0ff69dea78eb591465289d39e16f35e58389ae779d4f9455aff3a" }' \ 'http://localhost:8080/v1/sticker-packs/+431212131491291' # Send message with sticker (use pack_id:sticker_id format) curl -X POST -H "Content-Type: application/json" \ -d '{ "number": "+431212131491291", "recipients": ["+4354546464654"], "sticker": "9a32eda01a7a28574f2eb48668ae0dc4:42" }' \ 'http://localhost:8080/v2/send' ``` ### Remote Delete Delete a previously sent message. ```bash curl -X DELETE -H "Content-Type: application/json" \ -d '{ "recipient": "+4354546464654", "timestamp": 1697123456789 }' \ 'http://localhost:8080/v1/remote-delete/+431212131491291' # Response: {"timestamp":"1697123457000"} ``` ### API Configuration Get and set API configuration including logging level and trust mode. ```bash # Get configuration curl -X GET 'http://localhost:8080/v1/configuration' # Response: # { # "logging": { # "Level": "info" # } # } # Set configuration (change log level) curl -X POST -H "Content-Type: application/json" \ -d '{ "logging": { "Level": "debug" } }' \ 'http://localhost:8080/v1/configuration' # Get trust mode for account curl -X GET 'http://localhost:8080/v1/configuration/+431212131491291/settings' # Set trust mode (how to handle new identities) curl -X POST -H "Content-Type: application/json" \ -d '{ "trust_mode": "on-first-use" }' \ 'http://localhost:8080/v1/configuration/+431212131491291/settings' # Options: "on-first-use", "always", "never" ``` ### API Info and Health Get API version and health status. ```bash # About endpoint curl -X GET 'http://localhost:8080/v1/about' # Response: # { # "version": "v0.13.20", # "build": 12345, # "mode": "native", # "versions": ["v1", "v2"], # "capabilities": { # "sendMessages": ["v1", "v2"], # "groups": ["v1"] # } # } # Health check curl -X GET 'http://localhost:8080/v1/health' ``` ## Docker Deployment ### Basic Setup Run Signal CLI REST API with Docker and persistent configuration. ```bash # Create config directory mkdir -p $HOME/.local/share/signal-api # Start container (native mode - recommended) docker run -d --name signal-api \ --restart=always \ -p 8080:8080 \ -v $HOME/.local/share/signal-api:/home/.local/share/signal-cli \ -e 'MODE=native' \ bbernhard/signal-cli-rest-api # JSON-RPC mode (fastest, higher memory) docker run -d --name signal-api \ --restart=always \ -p 8080:8080 \ -v $HOME/.local/share/signal-api:/home/.local/share/signal-cli \ -e 'MODE=json-rpc' \ bbernhard/signal-cli-rest-api # Normal mode (slowest, lowest memory) docker run -d --name signal-api \ --restart=always \ -p 8080:8080 \ -v $HOME/.local/share/signal-api:/home/.local/share/signal-cli \ -e 'MODE=normal' \ bbernhard/signal-cli-rest-api ``` ### Docker Compose Complete docker-compose.yml configuration with environment variables. ```yaml version: "3" services: signal-cli-rest-api: image: bbernhard/signal-cli-rest-api:latest container_name: signal-api restart: always environment: - MODE=native # Options: normal, native, json-rpc - AUTO_RECEIVE_SCHEDULE=0 22 * * * # Auto-receive at 10pm daily - PORT=8080 - LOG_LEVEL=info # Options: debug, info, warn, error - DEFAULT_SIGNAL_TEXT_MODE=normal # Options: normal, styled - SIGNAL_CLI_UID=1000 - SIGNAL_CLI_GID=1000 - SWAGGER_IP=localhost - SWAGGER_HOST=localhost:8080 ports: - "8080:8080" volumes: - "./signal-cli-config:/home/.local/share/signal-cli" networks: - signal-net networks: signal-net: driver: bridge ``` ### Environment Variables ```bash # Execution mode (affects performance) MODE=native # normal, native, json-rpc # Auto-receive schedule (cron format) - only for normal/native modes AUTO_RECEIVE_SCHEDULE=0 22 * * * # Logging LOG_LEVEL=info # debug, info, warn, error # Server configuration PORT=8080 SWAGGER_IP=localhost SWAGGER_HOST=localhost:8080 SWAGGER_USE_HTTPS_AS_PREFERRED_SCHEME=false # Signal CLI configuration SIGNAL_CLI_CONFIG_DIR=/home/.local/share/signal-cli/ SIGNAL_CLI_UID=1000 SIGNAL_CLI_GID=1000 SIGNAL_CLI_CHOWN_ON_STARTUP=true # Text formatting DEFAULT_SIGNAL_TEXT_MODE=normal # normal or styled # JSON-RPC specific JSON_RPC_IGNORE_ATTACHMENTS=false JSON_RPC_IGNORE_STORIES=true JSON_RPC_TRUST_NEW_IDENTITIES=on-first-use # on-first-use, always, never # WebSocket webhook for receiving messages (json-rpc mode only) RECEIVE_WEBHOOK_URL= # Optional webhook URL for incoming messages ``` ## Integration Examples ### Python Client Library Using pysignalclirestapi for simplified Python integration. ```python from pysignalclirestapi import SignalCliRestApi # Initialize client signal = SignalCliRestApi("http://localhost:8080", "+431212131491291") # Send simple message signal.send_message("Hello!", ["+4354546464654"]) # Send with attachment with open("image.jpg", "rb") as f: attachment_data = f.read() signal.send_message("Check this!", ["+4354546464654"], base64_attachments=[attachment_data]) # Receive messages (blocking) for message in signal.receive_messages(): print(f"From: {message['source']}") print(f"Message: {message['message']}") # Download attachments if 'attachments' in message: for att in message['attachments']: data = signal.get_attachment(att['id']) with open(f"attachment_{att['id']}", "wb") as f: f.write(data) ``` ### Home Assistant Integration Send Signal notifications from Home Assistant automations. ```yaml # configuration.yaml notify: - name: signal platform: rest resource: http://localhost:8080/v2/send method: POST_JSON headers: Content-Type: application/json data: number: "+431212131491291" recipients: - "+4354546464654" message_param_name: message # automation.yaml automation: - alias: "Door opened notification" trigger: platform: state entity_id: binary_sensor.front_door to: "on" action: - service: notify.signal data: message: "Front door opened at {{ now().strftime('%H:%M') }}" # With image from camera - alias: "Motion detected with snapshot" trigger: platform: state entity_id: binary_sensor.motion_sensor to: "on" action: - service: camera.snapshot data: entity_id: camera.front_door filename: /tmp/snapshot.jpg - service: shell_command.send_signal_image # shell_command in configuration.yaml shell_command: send_signal_image: > curl -X POST -H "Content-Type: application/json" -d '{"message":"Motion detected!","number":"+431212131491291", "recipients":["+4354546464654"], "base64_attachments":["'$(base64 -w 0 /tmp/snapshot.jpg)'"]}' http://localhost:8080/v2/send ``` ### Node.js Bot Example Build a Signal chatbot using Node.js and WebSocket. ```javascript const WebSocket = require('ws'); const axios = require('axios'); const API_URL = 'http://localhost:8080'; const NUMBER = '+431212131491291'; // Connect to receive messages const ws = new WebSocket(`ws://localhost:8080/v1/receive/${NUMBER}`); ws.on('message', async (data) => { const msg = JSON.parse(data); if (msg.envelope?.dataMessage?.message) { const sender = msg.envelope.source; const text = msg.envelope.dataMessage.message; console.log(`Received: ${text} from ${sender}`); // Bot commands if (text.startsWith('/')) { await handleCommand(text, sender); } } }); async function handleCommand(command, sender) { let response = ''; if (command === '/help') { response = 'Available commands: /help, /time, /weather'; } else if (command === '/time') { response = `Current time: ${new Date().toLocaleString()}`; } else if (command === '/weather') { response = 'Weather: Sunny, 22°C'; } else { response = 'Unknown command. Type /help for options.'; } // Send response await axios.post(`${API_URL}/v2/send`, { message: response, number: NUMBER, recipients: [sender] }); } ws.on('open', () => console.log('Connected to Signal')); ws.on('error', (err) => console.error('WebSocket error:', err)); ``` ### Bash Script for Monitoring Simple monitoring script with Signal notifications. ```bash #!/bin/bash SIGNAL_API="http://localhost:8080/v2/send" MY_NUMBER="+431212131491291" ALERT_RECIPIENT="+4354546464654" send_signal() { local message="$1" curl -s -X POST -H "Content-Type: application/json" \ -d "{\"message\":\"${message}\",\"number\":\"${MY_NUMBER}\",\"recipients\":[\"${ALERT_RECIPIENT}\"]}" \ "${SIGNAL_API}" } # Check disk space DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//') if [ "$DISK_USAGE" -gt 90 ]; then send_signal "⚠️ ALERT: Disk usage at ${DISK_USAGE}%" fi # Check service status if ! systemctl is-active --quiet nginx; then send_signal "🚨 CRITICAL: nginx service is down!" fi # Check website uptime HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://example.com) if [ "$HTTP_CODE" != "200" ]; then send_signal "🔴 Website down! HTTP $HTTP_CODE" fi ``` ## Use Cases and Integration Patterns Signal CLI REST API serves as a bridge between Signal Messenger's secure communication protocol and automated systems, enabling developers to build Signal-powered features without mobile app dependencies. Primary use cases include home automation notifications (smart home alerts, security camera events, sensor triggers), server monitoring and DevOps alerting (service failures, deployment status, performance thresholds), two-factor authentication and verification systems, customer support chatbots, automated broadcast messaging, and IoT device notifications. The WebSocket-based receive endpoint enables bidirectional communication for interactive bots and command-and-control interfaces. Integration patterns typically follow three models: webhook-style push notifications (services POST to /v2/send), polling-based message retrieval (periodic GET requests or continuous receive subscriptions), and event-driven WebSocket connections for real-time bidirectional communication. The API's Docker-first architecture ensures easy deployment in containerized environments, while the three execution modes (normal/native/json-rpc) provide flexibility for different performance and resource requirements. Authentication and authorization are managed through phone number registration and device linking, with cryptographic keys stored in mounted volumes for persistence. Production deployments often place the API behind reverse proxies with TLS termination, implement rate limiting, and use environment-specific Signal accounts to separate development, staging, and production traffic.