I use the excellent signal-cli-rest-api by @bbernhard on
my server, and my home-assistant is configured to send me and the Home group
notifications of all kinds. Sending messages quickly is crucial for many of my
automations, so I'm running the API in json-rpc mode.
Recently, I wanted to add a way for Home Assistant to receive messages from us
to trigger automations (or stop others). However, when the signal-api is
running in json-rpc mode, the /v1/receive endpoint becomes websocket-only.
This is not supported by Home Assistant's signal_messenger
integration, which relies on REST API calls.
This project, signal-api-receiver, provides a solution by creating a
lightweight wrapper that:
- Consumes the websocket stream from the
/v1/receiveendpoint. - Stores received messages in memory.
- Exposes a REST API for retrieving those messages.
This approach allows Home Assistant to easily receive Signal messages and
trigger automations without requiring modifications to the existing
signal-cli-rest-api or the Home Assistant integration.
While developing signal-api-receiver solved my immediate need, there were
other potential approaches to this problem:
- Improve the Home Assistant integration with Signal to function properly with a Websocket.
- Propose a new endpoint to the
signal-cli-rest-apithat responds to REST.
These alternatives might be more comprehensive solutions in the long term, but creating the wrapper provided a more immediate and focused solution for my specific use case.
signal-api-receiver exposes the following API endpoints:
GET /receive/pop:- Returns one message at a time from the queue.
- If no messages are available, it returns a
204 No Contentstatus.
GET /receive/flush:- Returns all available messages as a list.
- If no messages are available, it returns an empty list (
[]).
signal-api-receiver is available as a Docker image on Docker Hub. This is the recommended way to run the application.
docker pull kalbasit/signal-api-receiver:latestHere's an example docker run command:
docker run -p 8105:8105 \
-e SIGNAL_ACCOUNT="your_signal_account" \
-e SIGNAL_API_URL="wss://your-signal-api-url" \
kalbasit/signal-api-receiver:latestExplanation:
-p 8105:8105: Maps port 8105 on the host to port 8105 in the container.-e SIGNAL_ACCOUNT="your_signal_account": Sets theSIGNAL_ACCOUNTenvironment variable. Replace with your actual Signal account.-e SIGNAL_API_URL="wss://your-signal-api-url": Sets theSIGNAL_API_URLenvironment variable. Replace with the URL of your Signal API.
Refer to the Docker Hub page for more information.
To run signal-api-receiver from source, you need to provide the following command-line flags:
Global Options:
--log-level <value>: Sets the logging level (default: "info"). Can be set using the$LOG_LEVELenvironment variable.
Options for the serve command:
--record-message-type <value>: Specifies which message types to record. Valid types are: "receipt", "typing", "data", "data-message", and "sync". This flag can be repeated to record multiple types (default: "data-message").--repeat-last-message: If enabled, repeats the last message if no new messages are available (applies to/receive/pop). This can be set using the$REPEAT_LAST_MESSAGEenvironment variable (default: false).--signal-account <value>: Required. Specifies your Signal account number. Can be set using the$SIGNAL_ACCOUNTenvironment variable.--signal-api-url <value>: Required. Specifies the URL of your Signal API, including the scheme (e.g.,wss://signal-api.example.com). Can be set using the$SIGNAL_API_URLenvironment variable.--server-addr <value>: Sets the address where the server will listen (default: ":8105"). Can be set using the$SERVER_ADDRenvironment variable.
By default, the server starts on :8105. You can change this using the --server-addr flag (e.g., --server-addr :8080).
You can see all available options by running:
signal-api-receiver serve --helpHere's an example of how to deploy signal-api-receiver on Kubernetes alongside existing signal-cli-rest-api deployment that is not shown here:
Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: signal-api-receiver
labels:
app: signal-receiver
tier: api
spec:
replicas: 1
selector:
matchLabels:
app: signal-receiver
tier: api
template:
metadata:
labels:
app: signal-receiver
tier: api
spec:
containers:
- image: kalbasit/signal-receiver:latest
name: signal-receiver
args:
- /bin/signal-api-receiver
- serve
- --signal-api-url=ws://signal-api.ns.svc:8080
- --signal-account=+19876543210
ports:
- containerPort: 8105
name: receiver-web
livenessProbe:
httpGet:
path: /healthz
port: receiver-web
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /healthz
port: receiver-web
initialDelaySeconds: 5
periodSeconds: 10Service
apiVersion: v1
kind: Service
metadata:
name: signal-api-receiver
labels:
app: signal-receiver
tier: api
spec:
type: ClusterIP
ports:
- name: receiver-web
port: 8105
selector:
app: signal-receiver
tier: apiTraefik IngressRoute
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: signal-api
spec:
entryPoints:
- web
- websecure
routes:
# This rule is for existing signal-cli-rest-api service that is not shown here.
- kind: Rule
match: Host(`signal-api.example.com`)
priority: 10
services:
- name: signal-api
port: http-web
# The new rule for signal-api-receiver.
- kind: Rule
match: Host(`signal-api.example.com`) && Path(`/receive`)
priority: 20
services:
- name: signal-api-receiver
port: receiver-web
tls:
secretName: signal-api-tlsThis project is licensed under the MIT License - see the LICENSE file for details.