Signal
The Signal integration lets users hold a bidirectional conversation with OctoMesh directly from the Signal messenger: they send messages (questions) and upload files (for example invoices), and receive answers back in the same chat. It is a good fit for external partners or suppliers who have no Microsoft Teams — Signal is end-to-end encrypted, mobile-first and needs no Microsoft/Google account.
It is built on a signal-cli-rest-api
bridge (a small Docker service that wraps signal-cli, holding a dedicated, registered Signal
number) and two mesh-adapter pipeline nodes:
| Node | Kind | Purpose |
|---|---|---|
FromSignal@1 | Trigger | Polls the bridge (GET /v1/receive/{number}) for inbound messages, downloads any attachment bytes (GET /v1/attachments/{id}) and starts the pipeline. Optional SenderFilter acts as a coarse allow-list. |
SignalSender@1 | Load | Sends a reply (with an optional attachment) back through the bridge (POST /v2/send). |
Signal has no official bot API. The bridge uses the community signal-cli, which needs a
dedicated registered phone number and is not guaranteed stable across Signal protocol
updates. It is well suited to internal/staff communication; weigh it for business-critical
supplier flows.
Architecture
- The user writes to the bridge's Signal number.
FromSignal@1polls the bridge, picks up new messages and downloads attachment bytes.- Your pipeline does whatever it needs (query data, run an AI prompt, stage an uploaded file …).
SignalSender@1posts the answer back through the bridge, which delivers it over Signal.
The adapter reaches the bridge over plain HTTP (apiUrl), so — unlike a webhook-based
approach — there is no inbound HTTPS endpoint to expose and no self-signed-certificate
problem between the bridge and the adapter.
Unified message shape
FromSignal@1 hands the pipeline a batch of normalized messages at $.Messages[]. The
attachment shape mirrors FromEmail@1 (AttachmentData), so the same downstream OCR /
document-import nodes apply:
$.Messages[0].Message — the message text (may be empty for a photo)
$.Messages[0].Source — sender: phone number, or an ACI/PNI UUID (see below)
$.Messages[0].SourceName — sender profile name (e.g. "Gerry")
$.Messages[0].Attachments[0].Data — base64 file content (image, PDF, …)
$.Messages[0].Attachments[0].ContentType
$.Messages[0].Attachments[0].FileName
Non-message events (delivery receipts, typing indicators, sync messages) carry no
dataMessage and are silently ignored by the trigger.
Configuration
Both nodes take the bridge URL and account number as plain node configuration — the local
bridge is unauthenticated, so there is no secret and no CK configuration entity to wire
(unlike Discord or the e-mail sender). The AI/MCP nodes in the pipeline keep their usual
Uses associations to AiConfiguration / ServiceAccountConfiguration.
| Setting | Node | Meaning |
|---|---|---|
apiUrl | both | Bridge base URL, e.g. http://localhost:8080 |
number | both | The bridge's registered account, e.g. +4366012345678 |
pollingIntervalSeconds | FromSignal@1 | Poll cadence (default 5). /v1/receive consumes messages on read, so no de-duplication is needed. |
senderFilter | FromSignal@1 | Optional contains-match allow-list on the sender |
recipient / recipientPath, message / messagePath | SignalSender@1 | Reply target and text |
Full property lists are in the API Reference → Adapters → MeshNodes → Trigger →
FromSignalNodeConfiguration and → Load → SignalSenderNodeConfiguration (generated
from the node configuration classes).
Sender identity & phone-number privacy
FromSignal@1 prefers the phone number (sourceNumber) as Source, so a reply and any
stored provenance are human-readable and replyable. However, Signal's phone-number-privacy
model means the number is only shared when the sender allows it — otherwise the envelope
carries only an ACI/PNI UUID plus the sender's profile name (SourceName). Both the number
and the UUID are valid recipients for SignalSender@1, so replies work either way; only the
displayed identifier differs.
Provenance
When a pipeline stages an inbound document as an UploadedDocument, set:
SourceChannel = SIGNAL(theDocumentSourceenum value), andSourceSender = $.full.msgSource(the sender captured at message level — see Setup).
Together these form a replyable origin, so a later follow-up can reach the sender back on Signal if a document can't be processed.
Continue with the step-by-step Setup.