Skip to main content

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:

NodeKindPurpose
FromSignal@1TriggerPolls 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@1LoadSends a reply (with an optional attachment) back through the bridge (POST /v2/send).
No official Signal bot API

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

  1. The user writes to the bridge's Signal number.
  2. FromSignal@1 polls the bridge, picks up new messages and downloads attachment bytes.
  3. Your pipeline does whatever it needs (query data, run an AI prompt, stage an uploaded file …).
  4. SignalSender@1 posts 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.

SettingNodeMeaning
apiUrlbothBridge base URL, e.g. http://localhost:8080
numberbothThe bridge's registered account, e.g. +4366012345678
pollingIntervalSecondsFromSignal@1Poll cadence (default 5). /v1/receive consumes messages on read, so no de-duplication is needed.
senderFilterFromSignal@1Optional contains-match allow-list on the sender
recipient / recipientPath, message / messagePathSignalSender@1Reply 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 (the DocumentSource enum value), and
  • SourceSender = $.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.