{"_id":"@bradsjm/n8n-nodes-signalwire","name":"@bradsjm/n8n-nodes-signalwire","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@bradsjm/n8n-nodes-signalwire","version":"0.1.0","description":"SignalWire nodes for n8n: live call control, media, transcription, Voice AI, messaging, call-flow triggers, communication events, and SWAIG tool gateway.","license":"MIT","author":{"name":"Jonathan Bradshaw","email":"jb@nrgup.net"},"homepage":"https://github.com/bradsjm/n8n-nodes-signalwire#readme","keywords":["n8n-community-node-package","n8n","signalwire"],"repository":{"type":"git","url":"https://github.com/bradsjm/n8n-nodes-signalwire.git"},"bugs":{"url":"https://github.com/bradsjm/n8n-nodes-signalwire/issues"},"engines":{"node":">=22.22.0"},"n8n":{"n8nNodesApiVersion":1,"strict":false,"credentials":["dist/credentials/SignalWireApi.credentials.js","dist/credentials/SignalWireWebhook.credentials.js"],"nodes":["dist/nodes/SignalWire/SignalWireCall.node.js","dist/nodes/SignalWire/SignalWireCallInteraction.node.js","dist/nodes/SignalWire/SignalWireCallMedia.node.js","dist/nodes/SignalWire/SignalWireTranscribeTranslate.node.js","dist/nodes/SignalWire/SignalWireVoiceAi.node.js","dist/nodes/SignalWire/SignalWireMessage.node.js","dist/nodes/SignalWire/SignalWireCallFlowTrigger.node.js","dist/nodes/SignalWire/SignalWireMessageTrigger.node.js","dist/nodes/SignalWire/SignalWireCommunicationEventTrigger.node.js","dist/nodes/SignalWire/SignalWireSwaigToolsTrigger.node.js","dist/nodes/SignalWire/SignalWireCallFlowResponse.node.js"]},"devDependencies":{"@n8n/node-cli":"^0.40.3","eslint":"9.29.0","prettier":"3.9.6","release-it":"^20.2.1","typescript":"5.9.3","vitest":"^4.1.10"},"peerDependencies":{"n8n-workflow":">=2.31.3"},"dependencies":{"@langchain/core":"1.2.0"},"scripts":{"build":"n8n-node build","build:watch":"tsc --watch","docker:dev":"node scripts/docker-dev.mjs","dev":"n8n-node dev","lint":"n8n-node lint","lint:fix":"n8n-node lint --fix","release":"n8n-node release","test":"vitest run"},"_nodeVersion":"22.23.1","_id":"@bradsjm/n8n-nodes-signalwire@0.1.0","dist":{"integrity":"sha512-yNh+eeB+tsy15zlIYWB7MHUOu57HU+FIzE0oqFh42rScuTYqB+TuyMKm81veIej6y3uIjcEVbrfMjKHM/VN8pQ==","shasum":"59191260feef5cfb2d870462ea15568b11d8aaf5","tarball":"https://registry.npmjs.org/@bradsjm/n8n-nodes-signalwire/-/n8n-nodes-signalwire-0.1.0.tgz","fileCount":113,"unpackedSize":868955,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDiIch/NIFF08d3NTt0dvJOtYyzGOQq674cZsnXjReFkAIgWv+OSCGOxiTrVL0hWzV0IXbhkIP2PchXGJ+crPgHltc="}]},"_npmUser":{"name":"bradsjm","email":"jb@nrgup.net"},"directories":{},"maintainers":[{"name":"bradsjm","email":"jb@nrgup.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/n8n-nodes-signalwire_0.1.0_1784852999814_0.8781481663049175"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-24T00:29:59.606Z","0.1.0":"2026-07-24T00:29:59.994Z","modified":"2026-07-24T00:30:00.205Z"},"maintainers":[{"name":"bradsjm","email":"jb@nrgup.net"}],"description":"SignalWire nodes for n8n: live call control, media, transcription, Voice AI, messaging, call-flow triggers, communication events, and SWAIG tool gateway.","homepage":"https://github.com/bradsjm/n8n-nodes-signalwire#readme","keywords":["n8n-community-node-package","n8n","signalwire"],"repository":{"type":"git","url":"https://github.com/bradsjm/n8n-nodes-signalwire.git"},"author":{"name":"Jonathan Bradshaw","email":"jb@nrgup.net"},"bugs":{"url":"https://github.com/bradsjm/n8n-nodes-signalwire/issues"},"license":"MIT","readme":"# SignalWire nodes for n8n\n\nUse SignalWire from n8n to place and control calls, send and receive messages, process communication events, build voice AI workflows, and expose n8n AI tools through SWAIG.\n\nThis package provides 11 nodes for the native SignalWire REST and SWML interfaces. It is designed for people who know the basics of building an n8n workflow but do not want to hand-build SignalWire API requests.\n\n## Table of contents\n\n- [What you can build](#what-you-can-build)\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Set up credentials](#set-up-credentials)\n- [Choose a node](#choose-a-node)\n- [Core concepts](#core-concepts)\n- [Example workflows](#example-workflows)\n  - [Place an outbound call](#place-an-outbound-call)\n  - [Handle an inbound call with SWML](#handle-an-inbound-call-with-swml)\n  - [Reply to an inbound message](#reply-to-an-inbound-message)\n  - [Track message delivery](#track-message-delivery)\n  - [Transcribe an active call](#transcribe-an-active-call)\n  - [Expose n8n AI tools to SignalWire](#expose-n8n-ai-tools-to-signalwire)\n- [Action node reference](#action-node-reference)\n- [Trigger and response reference](#trigger-and-response-reference)\n- [Outputs and errors](#outputs-and-errors)\n- [Webhook deployment](#webhook-deployment)\n- [Security](#security)\n- [Troubleshooting](#troubleshooting)\n- [Development](#development)\n- [Helpful links](#helpful-links)\n\n## What you can build\n\nExamples include:\n\n- Place an outbound PSTN or SIP call and supply its call instructions as a URL or inline SWML.\n- Play audio or text-to-speech, collect DTMF or speech, and detect fax, voicemail, or digits.\n- Start, pause, resume, or stop recordings, streams, and media taps.\n- Transcribe or translate a live conversation and process its callbacks in another n8n execution.\n- Build Calling SWML for a SignalWire speaking agent or AI sidecar.\n- Send SMS/MMS, receive inbound messages, and track delivery events.\n- Handle an inbound call synchronously: SignalWire requests instructions and the n8n workflow returns SWML.\n- Make existing n8n AI Tool nodes callable by a SignalWire AI agent through SWAIG.\n\n## Requirements\n\n- A [SignalWire account and Space](https://signalwire.com/docs/platform/signing-up-for-a-space).\n- A self-hosted n8n instance running **n8n 2.31.3 or newer**.\n- **Node.js 22.22.0 or newer** where the community package is installed.\n- A public HTTPS n8n webhook origin for inbound calls, messages, callbacks, or SWAIG.\n\nThe package uses capabilities that do not currently satisfy every n8n Cloud community-node policy, including generic HTTP credentials and a runtime LangChain dependency. Self-hosted n8n is the supported deployment target.\n\n## Installation\n\nIn n8n:\n\n1. Open **Settings > Community Nodes**.\n2. Select **Install**.\n3. Enter `@bradsjm/n8n-nodes-signalwire`.\n4. Confirm the installation, then restart n8n if prompted.\n\nSee n8n's [community-node installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) for installation and upgrade details.\n\nFor local package development, use the commands in [Development](#development). Do not install the package globally.\n\n## Set up credentials\n\nSignalWire REST requests and inbound webhook verification use different credentials. Most workflows need one or both.\n\n### SignalWire API\n\nUse **SignalWire API** on the six action nodes. Find these values under **SignalWire Dashboard > API Credentials**.\n\n| Field          | Value                                                                                      |\n| -------------- | ------------------------------------------------------------------------------------------ |\n| **Space**      | Your Space hostname, such as `example.signalwire.com` — without `https://` or an API path. |\n| **Project ID** | The Project UUID used as the HTTP Basic username.                                          |\n| **API Token**  | A server-side API token used as the HTTP Basic password.                                   |\n\nCreate a least-privileged token:\n\n- Enable the **Voice** scope for the five Calling action nodes.\n- Enable the **Messaging** scope for SignalWire Message.\n\nSee [SignalWire API credentials](https://signalwire.com/docs/platform/your-signalwire-api-space).\n\n### SignalWire Webhook\n\nUse **SignalWire Webhook** on all four inbound trigger/gateway nodes. It contains one field:\n\n| Field           | Value                                                                           |\n| --------------- | ------------------------------------------------------------------------------- |\n| **Signing Key** | The project signing key shown under **SignalWire Dashboard > API Credentials**. |\n\nThe signing key verifies that an inbound request came from SignalWire. It is not the API token and is never sent back to SignalWire. See [SignalWire webhook verification](https://signalwire.com/docs/platform/webhooks#verify-webhook-signature).\n\n### Optional endpoint credentials\n\nTwo operations can use n8n's generic credentials:\n\n- **HTTP Basic Auth** for authenticated SIP Dial or SIP REFER endpoints.\n- **HTTP Bearer Auth** for an authorized call-audio WebSocket stream.\n\nThese credentials protect the destination endpoint. They are separate from the SignalWire API credential.\n\n## Choose a node\n\n### Actions\n\n| Node                                  | Choose it when you need to…                                                                                                                              |\n| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **SignalWire Call**                   | Start a call, replace its SWML, transfer or disconnect it, send SIP REFER, end it, or emit a user event.                                                 |\n| **SignalWire Call Interaction**       | Play audio/TTS/ringtones, collect DTMF or speech, or run machine/fax/digit detection.                                                                    |\n| **SignalWire Call Media**             | Record or export call audio through recording, WebSocket stream, or RTP/WebSocket tap; control denoise or stop fax. To play audio, use Call Interaction. |\n| **SignalWire Transcribe & Translate** | Start/stop background transcription, control live transcription, or translate a live conversation.                                                       |\n| **SignalWire Voice AI**               | Build SWML for a speaking agent/sidecar or control an AI session already running on a call.                                                              |\n| **SignalWire Message**                | Send SMS/MMS or reply to an item from Inbound Message Trigger.                                                                                           |\n\n### Triggers and terminal response\n\n| Node                                       | Choose it when you need to…                                                              |\n| ------------------------------------------ | ---------------------------------------------------------------------------------------- |\n| **SignalWire Call Flow Trigger**           | Receive an inbound Calling request and return SWML synchronously from the same workflow. |\n| **SignalWire Inbound Message Trigger**     | Receive and normalize an inbound SMS/MMS while acknowledging it immediately.             |\n| **SignalWire Communication Event Trigger** | Receive asynchronous call, message, media, transcript, stream, or AI-sidecar callbacks.  |\n| **SignalWire SWAIG Tools Trigger**         | Expose connected n8n AI tools through a synchronous SignalWire SWAIG webhook.            |\n| **SignalWire Call Flow Response**          | Validate and return the final Calling SWML document from a Call Flow Trigger workflow.   |\n\n## Core concepts\n\n### Call ID and Control ID\n\nA **Call ID** is SignalWire's UUID for an active call. You commonly obtain it from:\n\n- `callId` in a SignalWire action output,\n- `callId` from Communication Event Trigger, or\n- `call_id` in the raw Call Flow Trigger payload.\n\nA **Control ID** is your name for a long-running action on that call, such as a playback, recording, collection, stream, or detector. Choose it when starting the action, then reuse the same value to pause, resume, or stop that action.\n\nExample flow:\n\n```text\nCall Interaction: Playback / Start\n  Call ID:    ={{ $json.callId }}\n  Control ID: welcome-message\n\nCall Interaction: Playback / Stop\n  Call ID:    ={{ $json.callId }}\n  Control ID: welcome-message\n```\n\nUse a predictable Control ID when later workflow steps must reference the action.\n\n### Main workflow versus callback workflow\n\nA SignalWire REST action returns when SignalWire accepts the command. It does not wait for a call, recording, stream, message, or transcript to finish.\n\nUse a **Communication Event Trigger** in a separate workflow when you need the eventual result. Copy that trigger's Production URL into the action's **Status URL**, **Status Callback**, **Webhook URL**, or **Summary Webhook URL** field.\n\n### Test URL versus Production URL\n\nn8n exposes two webhook URLs:\n\n- **Test URL** works only while the editor is listening for a test event.\n- **Production URL** works while the workflow is active.\n\nConfigure SignalWire with the **Production URL** for deployed workflows.\n\n### SWML\n\n[SWML](https://signalwire.com/docs/swml) is SignalWire's JSON/YAML instruction language. Calling and Messaging are different SWML document types with different methods.\n\nA minimal Calling document looks like this:\n\n```json\n{\n\t\"version\": \"1.0.0\",\n\t\"sections\": {\n\t\t\"main\": [{ \"answer\": {} }, { \"play\": { \"url\": \"say:Hello from n8n.\" } }, { \"hangup\": {} }]\n\t}\n}\n```\n\nSee the [Calling SWML reference](https://signalwire.com/docs/swml/reference/calling) before adding methods or variables.\n\n## Example workflows\n\n### Place an outbound call\n\nUse this when n8n should initiate a call and SignalWire should run a short inline SWML script.\n\n```mermaid\nflowchart LR\n    A[\"Manual Trigger or app event\"] --> B[\"SignalWire Call: Start / Dial\"]\n    B --> C[\"SignalWire outbound call\"]\n    C -. \"Optional status callbacks\" .-> D[\"Communication Event Trigger\"]\n```\n\n1. Add **SignalWire Call** after a Manual Trigger or application event.\n2. Select **Start > Dial**.\n3. Choose a **SignalWire API** credential with Voice scope.\n4. Set **From** to your SignalWire number or valid source address.\n5. Set **Destination Source** to **Address** and enter the recipient in **To**.\n6. Set **Instruction Source** to **Inline SWML**.\n7. Enter Calling SWML, for example:\n\n```json\n{\n\t\"version\": \"1.0.0\",\n\t\"sections\": {\n\t\t\"main\": [{ \"play\": { \"url\": \"say:Your appointment is tomorrow at ten AM.\" } }, { \"hangup\": {} }]\n\t}\n}\n```\n\nThe output includes the accepted call's `callId`. Use that value in later Call Interaction, Call Media, Transcribe & Translate, or Voice AI nodes.\n\nFor provider fields and supported commands, see [Send call commands](https://signalwire.com/docs/apis/rest/calls/call-commands).\n\n### Handle an inbound call with SWML\n\nUse this when SignalWire should ask n8n what an inbound call should do.\n\n```mermaid\nflowchart LR\n    S[\"Inbound call from SignalWire\"] --> A[\"SignalWire Call Flow Trigger\"]\n    A --> B[\"Optional n8n logic\"]\n    B --> C[\"SignalWire Call Flow Response\"]\n    C --> S\n```\n\n1. Add **SignalWire Call Flow Trigger** and choose a **SignalWire Webhook** credential.\n2. End every successful path with **SignalWire Call Flow Response**.\n3. For a simple response, choose **Define Below** in the response node and enter a valid Calling SWML document.\n4. Activate the workflow and copy the trigger's **Production URL**.\n5. In the SignalWire Dashboard, create a SWML Resource that handles calls using an **External URL**.\n6. Use the n8n Production URL as its primary script URL, then assign that Resource to the desired phone number.\n\nThe response node requires exactly one input item. It can read SWML from:\n\n- the whole input item,\n- a dot-notated property such as `swml`, or\n- JSON entered directly in the node.\n\nSee [SignalWire Webhooks](https://signalwire.com/docs/platform/webhooks) and [Handle incoming calls from code](https://signalwire.com/docs/swml/guides/remote-server).\n\n### Reply to an inbound message\n\n```mermaid\nflowchart LR\n    S[\"Inbound SMS or MMS\"] --> A[\"SignalWire Inbound Message Trigger\"]\n    A --> B[\"Optional routing or lookup\"]\n    B --> C[\"SignalWire Message: Reply\"]\n    C --> S\n```\n\n1. Add **SignalWire Inbound Message Trigger** and choose a **SignalWire Webhook** credential.\n2. Add **SignalWire Message**, then select **Reply**.\n3. `From` defaults to the number that received the message: `={{ $json.to }}`.\n4. `To` defaults to the original sender: `={{ $json.from }}`.\n5. Enter a message body and/or up to eight public media URLs.\n6. Activate the workflow and configure the trigger's Production URL as the phone number's inbound Messaging handler in SignalWire.\n\nThe trigger acknowledges the inbound message immediately with Messaging `receive` SWML, then emits normalized message data to the workflow.\n\nSee the [Messaging SWML overview](https://signalwire.com/docs/swml/reference/messaging) and [Send a message](https://signalwire.com/docs/apis/rest/messages/create-message).\n\n### Track message delivery\n\nUse two workflows:\n\n```mermaid\nflowchart LR\n    A[\"SignalWire Message: Send\"] --> S[\"SignalWire Messaging\"]\n    S -. \"Delivery callback\" .-> B[\"Communication Event Trigger: Message Delivery\"]\n    B --> C[\"Store, notify, or update a record\"]\n```\n\n**Send workflow**\n\n1. Add **SignalWire Message > Send**.\n2. Enter From, To, and Body/Media.\n3. Set **Status Callback** to the Production URL from the event workflow below.\n\n**Event workflow**\n\n1. Add **SignalWire Communication Event Trigger**.\n2. Choose **Message Delivery** under Event Families.\n3. Activate the workflow and copy its Production URL into the sending node.\n4. Process `eventType`, `messageId`, and the original `payload` downstream.\n\nA status of `sent` means SignalWire handed the message off successfully. `delivered` depends on a carrier delivery receipt and is not available for every channel or message type. See [Message status callback](https://signalwire.com/docs/apis/rest/messages/webhooks/message-status-callback).\n\n### Transcribe an active call\n\n```mermaid\nflowchart LR\n    A[\"Earlier SignalWire action or event\"] --> B[\"SignalWire Transcribe & Translate\"]\n    B --> S[\"Active SignalWire call\"]\n    S -. \"Transcript callback\" .-> C[\"Communication Event Trigger: Transcript\"]\n    C --> D[\"Store transcript or run follow-up logic\"]\n```\n\n1. Obtain an active `callId` from an earlier SignalWire action or event.\n2. Add **SignalWire Transcribe & Translate**.\n3. Choose one of:\n   - **Background Transcription** for a completion callback after transcription ends.\n   - **Live Transcription** for real-time utterance events and optional summaries.\n   - **Live Translation** to translate, summarize, or inject translated speech.\n4. Select the caller direction, language, speech engine, and relevant tuning options.\n5. To process callbacks in n8n, set the node's callback/webhook field to a **Communication Event Trigger** Production URL and enable **Transcript** in that trigger.\n\nRelevant references:\n\n- [Transcript status callback](https://signalwire.com/docs/apis/rest/calls/webhooks/transcribe-status-callback)\n- [Calling `live_transcribe`](https://signalwire.com/docs/swml/reference/calling/live-transcribe)\n- [Calling `live_translate`](https://signalwire.com/docs/swml/reference/calling/live-translate)\n\n### Expose n8n AI tools to SignalWire\n\nUse **SignalWire SWAIG Tools Trigger** when a SignalWire speaking agent or AI sidecar should invoke n8n AI Tool nodes.\n\n```mermaid\nflowchart LR\n    T1[\"n8n AI Tool: CRM lookup\"] --> G[\"SignalWire SWAIG Tools Trigger\"]\n    T2[\"n8n AI Tool: Create ticket\"] --> G\n    S[\"SignalWire AI agent or sidecar\"] -->|Tool request| G\n    G -->|Tool response| S\n```\n\n1. Add the SWAIG trigger and select **Speaking Agent** or **Sidecar**.\n2. Connect one or more n8n AI Tool nodes to its **Tools** input. Up to 32 connections are supported.\n3. Optionally limit exposure with comma-separated exact names in **Allowed Tools**.\n4. Activate the workflow and copy its Production URL.\n5. Use that URL as the published SWAIG gateway/tool webhook in your SignalWire AI configuration or in **SignalWire Voice AI > Tool Source > SWAIG Gateway**.\n\nThe endpoint supports:\n\n- signature discovery through `action: \"get_signature\"`, and\n- synchronous tool invocation that returns `{ \"response\": \"...\" }`, optionally with validated SWAIG actions.\n\nThe trigger has no ordinary workflow output: the connected tool result is returned directly to SignalWire during the webhook request.\n\nSee the [SWAIG guide](https://signalwire.com/docs/swml/guides/swaig), [AI SWAIG tool webhook](https://signalwire.com/docs/apis/rest/calls/webhooks/ai-swaig-tool-webhook), and [AI sidecar SWAIG tool webhook](https://signalwire.com/docs/apis/rest/calls/webhooks/ai-sidecar-swaig-tool-webhook).\n\n## Action node reference\n\nThe five Calling action nodes cover the complete current `POST /api/calling/calls` command surface. SignalWire Message uses the separate native Messaging endpoint.\n\n### SignalWire Call\n\n| Activity / operation    | SignalWire command   |\n| ----------------------- | -------------------- |\n| Start / Dial            | `dial`               |\n| Change Flow / Update    | `update`             |\n| Route / Transfer        | `calling.transfer`   |\n| Route / Disconnect      | `calling.disconnect` |\n| Route / Refer           | `calling.refer`      |\n| End / End               | `calling.end`        |\n| Emit Event / User Event | `calling.user_event` |\n\nDial accepts E.164 numbers, SIP URIs, client addresses, remote SWML URLs, and inline SWML. Transfer accepts an address, SWML URL, or inline SWML. REFER is for SIP calls and can use optional HTTP Basic Auth.\n\n### SignalWire Call Interaction\n\n| Activity / operation   | SignalWire command                   |\n| ---------------------- | ------------------------------------ |\n| Playback / Start       | `calling.play`                       |\n| Playback / Pause       | `calling.play.pause`                 |\n| Playback / Resume      | `calling.play.resume`                |\n| Playback / Stop        | `calling.play.stop`                  |\n| Playback / Set Volume  | `calling.play.volume`                |\n| Input / Start          | `calling.collect`                    |\n| Input / Stop           | `calling.collect.stop`               |\n| Input / Restart Timers | `calling.collect.start_input_timers` |\n| Detection / Start      | `calling.detect`                     |\n| Detection / Stop       | `calling.detect.stop`                |\n\nPlayback items can be audio URLs, text-to-speech, silence, or country-specific ringtones. Input supports DTMF, speech, or both. Detection supports answering-machine, fax, and digit detection.\n\n### SignalWire Call Media\n\n| Activity / operation    | SignalWire command         |\n| ----------------------- | -------------------------- |\n| Recording / Start       | `calling.record`           |\n| Recording / Pause       | `calling.record.pause`     |\n| Recording / Resume      | `calling.record.resume`    |\n| Recording / Stop        | `calling.record.stop`      |\n| Stream / Start          | `calling.stream`           |\n| Stream / Stop           | `calling.stream.stop`      |\n| Tap / Start             | `calling.tap`              |\n| Tap / Stop              | `calling.tap.stop`         |\n| Noise Reduction / Start | `calling.denoise`          |\n| Noise Reduction / Stop  | `calling.denoise.stop`     |\n| Fax / Stop Send         | `calling.send_fax.stop`    |\n| Fax / Stop Receive      | `calling.receive_fax.stop` |\n\nUse Stream for a WebSocket audio destination. Stream codecs are endpoint-specific and accept freeform values such as `PCMU`, `PCMA`, or `OPUS`. Use Tap for an RTP or WebSocket media target with explicit codec/packetization settings.\n\n### SignalWire Transcribe & Translate\n\n| Activity / operation                              | SignalWire command        |\n| ------------------------------------------------- | ------------------------- |\n| Background Transcription / Start                  | `calling.transcribe`      |\n| Background Transcription / Stop                   | `calling.transcribe.stop` |\n| Live Transcription / Start, Stop, Summarize       | `calling.live_transcribe` |\n| Live Translation / Start, Stop, Summarize, Inject | `calling.live_translate`  |\n\nStart operations expose language, call direction, event webhook, speech engine, voice-activity detection, and summary options. The live commands carry the selected action inside the request.\n\n### SignalWire Voice AI\n\n| Runtime / operation                                 | SignalWire command                 |\n| --------------------------------------------------- | ---------------------------------- |\n| Speaking Agent / Build Instructions                 | Local SWML output; no REST request |\n| Speaking Agent / Hold                               | `calling.ai_hold`                  |\n| Speaking Agent / Unhold                             | `calling.ai_unhold`                |\n| Speaking Agent / Message, Reset, Update Global Data | `calling.ai_message`               |\n| Speaking Agent / Stop                               | `calling.ai.stop`                  |\n| Sidecar / Build Instructions                        | Local SWML output; no REST request |\n| Sidecar / Attach, Summarize                         | `calling.ai_sidecar`               |\n| Sidecar / Poke                                      | `calling.ai_sidecar.poke`          |\n| Sidecar / Ask                                       | `calling.ai_sidecar.ask`           |\n| Sidecar / Stop                                      | `calling.ai_sidecar.stop`          |\n| Sidecar / Status                                    | `calling.ai_sidecar.status`        |\n\n**Build Instructions** emits a raw Calling SWML document and does not require an API credential. Other operations control an AI session that already exists on the specified call and return the normal action output envelope.\n\nTool definitions can come from an HTTPS MCP server, a published SWAIG gateway, or manual SWAIG JSON. Manual JSON containing credential fields is rejected.\n\nReferences:\n\n- [Calling `ai`](https://signalwire.com/docs/swml/reference/calling/ai)\n- [Calling `ai_sidecar`](https://signalwire.com/docs/swml/reference/calling/ai-sidecar)\n- [SWAIG reference](https://signalwire.com/docs/swml/reference/calling/ai/swaig)\n\n### SignalWire Message\n\n| Operation | Behavior                                                                                        |\n| --------- | ----------------------------------------------------------------------------------------------- |\n| Send      | Send SMS/MMS from `From` to `To`.                                                               |\n| Reply     | Reverse the normalized inbound message addresses by default and send through the same endpoint. |\n\nAt least one of Body or Media is required. Media must be available over public HTTP/HTTPS, with a maximum of eight URLs. Adding media makes the message MMS; **Send as MMS** forces MMS even without media.\n\nCustom variables support up to 20 key/value pairs. Keys must start with a letter or underscore and contain only letters, numbers, and underscores. Values must be non-empty strings of at most 1024 bytes. When a Status Callback is set, SignalWire includes custom variables in callback payloads for correlation.\n\n## Trigger and response reference\n\n### Call Flow Trigger\n\n- Verifies the raw signed request.\n- Validates the required Calling webhook shape while allowing additional provider fields.\n- Emits the provider payload unchanged.\n- Waits for the workflow's last node and returns its first JSON item as the HTTP response.\n- Must finish with a valid Calling SWML document, normally through Call Flow Response.\n\n### Inbound Message Trigger\n\n- Verifies and validates inbound Messaging SWML payloads.\n- Returns an immediate Messaging `receive` acknowledgement.\n- Emits normalized camelCase fields:\n  `messageId`, `projectId`, `spaceId`, `direction`, `type`, `from`, `to`, `body`, `media`, `segments`, `timestamp`, `params`, `vars`, and `raw`.\n- `raw` preserves the original provider representation.\n\n### Communication Event Trigger\n\nSelect **All** or one or more families:\n\n- AI Sidecar\n- Call Lifecycle\n- Detection\n- Input\n- Message Delivery\n- Playback\n- Recording\n- Stream\n- Transcript\n\nKnown events produce this normalized envelope:\n\n```json\n{\n\t\"category\": \"transcript\",\n\t\"eventType\": \"calling.transcript.completed\",\n\t\"occurredAt\": 1784800000,\n\t\"callId\": \"00000000-0000-4000-8000-000000000000\",\n\t\"controlId\": null,\n\t\"messageId\": null,\n\t\"sessionId\": null,\n\t\"payload\": {}\n}\n```\n\nThe trigger acknowledges with HTTP 200 before downstream execution. A filtered event is acknowledged but does not run the workflow. Unknown event shapes run only when **All** is selected.\n\n### SWAIG Tools Trigger\n\n- Requires one or more connected AI Tool inputs.\n- Supports speaking-agent and sidecar webhook contracts.\n- Returns tool signatures and results synchronously.\n- Has no `main` workflow output.\n- Returns generic error text for invalid tool input or tool failures; it does not expose exceptions, credentials, raw arguments, or call metadata.\n\n### Call Flow Response\n\n- Requires exactly one input item.\n- Accepts the whole item, a dot-notated property, or JSON defined in the node.\n- Requires `version: \"1.0.0\"`, an object-valued `sections`, and an array-valued `sections.main`.\n- Returns the SWML document itself, not `{ \"swml\": ... }`.\n\n## Outputs and errors\n\n### REST action output\n\nExcept for Voice AI **Build Instructions**, successful action nodes emit:\n\n```json\n{\n\t\"accepted\": true,\n\t\"activity\": \"recording\",\n\t\"operation\": \"start\",\n\t\"callId\": \"00000000-0000-4000-8000-000000000000\",\n\t\"controlId\": \"recording-1\",\n\t\"messageId\": null,\n\t\"status\": \"queued\",\n\t\"response\": {}\n}\n```\n\nCorrelation fields are always present and use `null` when unavailable. `response` contains the redacted provider response.\n\nIf **Continue On Fail** is enabled, the node emits an item containing `error` and continues with later input items. Otherwise, validation and provider errors stop execution.\n\nCommon validation messages are:\n\n```text\nMissing required parameter: callId\nInvalid parameter statusUrl: must be an http or https URL\nInvalid parameter callId: must be a canonical UUID\n```\n\nWhen a call is accepted but transitions immediately to `failed`, inspect the SignalWire Dashboard and provider response for `failure_reason`.\n\n## Webhook deployment\n\nFor any inbound trigger:\n\n1. Configure n8n's public webhook origin correctly.\n2. Add the trigger and its SignalWire Webhook credential.\n3. Activate the workflow.\n4. Copy the **Production URL**.\n5. Place that exact URL in the corresponding SignalWire Resource or callback field.\n6. Send a test interaction and inspect the n8n execution plus the SignalWire log.\n\nWhen n8n is behind a reverse proxy, set:\n\n```text\nN8N_WEBHOOK_URL=https://automation.example.com/\nN8N_PROXY_HOPS=1\n```\n\n`N8N_WEBHOOK_URL` must include any public path prefix. Restart n8n after changing it. Signature verification depends on the exact public URL SignalWire called, so an incorrect scheme, host, port, or path causes HTTP 401.\n\n## Security\n\n- Store Project IDs, API tokens, signing keys, SIP passwords, and bearer tokens only in n8n credentials.\n- Do not put secrets in node fields, expressions, SWML, workflow output, or execution logs.\n- Every inbound SignalWire trigger verifies the signature against the exact raw request body before JSON parsing or tool execution.\n- Use API tokens with only the Voice or Messaging scopes required by the workflow.\n- Use HTTPS for public callbacks, SWML URLs, media URLs, MCP servers, and SWAIG gateways.\n- Provider error details and workflow output are scrubbed of known API/SIP/stream secrets.\n\n## Troubleshooting\n\n### HTTP 401 from a trigger\n\n- Confirm that the SignalWire Webhook credential contains the project's signing key, not its API token.\n- Confirm `N8N_WEBHOOK_URL` matches the public URL exactly.\n- Check reverse-proxy path and `N8N_PROXY_HOPS` configuration.\n- Make sure SignalWire is calling the Production URL shown by the node.\n\n### HTTP 400 from a trigger\n\nThe signature was accepted, but the body was malformed or did not match the expected SignalWire webhook family. Confirm that the callback was sent to the correct trigger:\n\n- Calling document request → Call Flow Trigger\n- Inbound SMS/MMS → Inbound Message Trigger\n- Status/transcript/stream/sidecar event → Communication Event Trigger\n- Speaking-agent or sidecar tool call → SWAIG Tools Trigger\n\n### REST node returns 401 or 403\n\n- Verify Space, Project ID, and API Token.\n- Confirm the token has Voice or Messaging scope for the operation.\n- Enter only the hostname in Space, not `https://` or `/api/...`.\n\n### A follow-up call operation cannot find the action\n\nConfirm both identifiers:\n\n- Call ID must reference the active call.\n- Control ID must exactly match the value used when the playback, recording, collection, detector, stream, or tap was started.\n\n### No callback workflow execution\n\n- Activate the callback workflow.\n- Use its Production URL in the originating action.\n- Select the relevant Communication Event family or use All.\n- Check the SignalWire callback/log and the n8n execution list.\n\n### Call Flow returns invalid instructions\n\n- End every successful path with Call Flow Response.\n- Ensure exactly one item reaches the response node.\n- Return the SWML document itself, not a wrapper object.\n- Validate `version`, `sections`, and `sections.main` against the [Calling SWML reference](https://signalwire.com/docs/swml/reference/calling).\n\n## Development\n\nThe commands below are for contributors working on this repository, not normal n8n installation.\n\nUse Node.js 22.22.0 or newer and pnpm 11.16.0:\n\n```bash\npnpm install\npnpm dev             # n8n-node development environment\npnpm build           # compile/package to dist/\npnpm build:watch     # TypeScript watch mode\npnpm test            # run 210 Vitest tests\npnpm lint            # n8n community-node lint rules\npnpm lint:fix         # apply supported fixes\npnpm docker:dev      # pack and run single-instance n8n on port 5678\n```\n\nFor a focused test:\n\n```bash\npnpm exec vitest run test/actions/call.test.ts\npnpm exec vitest run test/triggers/swaigTools.test.ts\n```\n\nThe repository also contains a PostgreSQL/Redis queue-mode smoke environment under `test/queue/` for validating synchronous Call Flow and SWAIG behavior across main/worker processes.\n\n`pnpm lint` currently reports seven known n8n Cloud-policy conflicts involving generic HTTP credential reuse, the runtime LangChain dependency/import, and the minimum `n8n-workflow` peer version. Treat additional lint findings as regressions.\n\n## Helpful links\n\n### SignalWire\n\n- [SignalWire documentation](https://signalwire.com/docs)\n- [API credentials](https://signalwire.com/docs/platform/your-signalwire-api-space)\n- [Webhooks](https://signalwire.com/docs/platform/webhooks)\n- [REST API overview](https://signalwire.com/docs/apis)\n- [Calling command reference](https://signalwire.com/docs/apis/rest/calls/call-commands)\n- [Send a message](https://signalwire.com/docs/apis/rest/messages/create-message)\n- [SWML introduction](https://signalwire.com/docs/swml)\n- [Calling SWML](https://signalwire.com/docs/swml/reference/calling)\n- [Messaging SWML](https://signalwire.com/docs/swml/reference/messaging)\n- [SWAIG guide](https://signalwire.com/docs/swml/guides/swaig)\n- [AI sidecar](https://signalwire.com/docs/swml/reference/calling/ai-sidecar)\n\n### n8n\n\n- [n8n documentation](https://docs.n8n.io/)\n- [Install community nodes](https://docs.n8n.io/integrations/community-nodes/installation/)\n- [Webhook node behavior](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/)\n\n### Project\n\n- [Source and issues](https://github.com/bradsjm/n8n-nodes-signalwire)\n","readmeFilename":"","_rev":"1-8e503fe4a35b11df065411bf23eea05b"}