T
Telephony SOPKnowledge Base
Search
← All topics

SignalWire Call Flow Builder (CFB)

Drag-and-drop visual editor in the SignalWire portal. Generates valid SWML under the hood. Node taxonomy (Handle Call, AI Agent, Forward, Gather, Conditions, Request, Set Variables), variable substitution with `%{...}`, building and deploying flows, and CFB-vs-SWML decision rules.

signalwirecall-flow-builderno-codeivrvisual-editor
Agent trigger phrases: call flow builder · CFB SignalWire · visual call flow · no-code IVR · AI Agent node · Forward to Phone node · Gather Input speech DTMF · Set Variables node

SignalWire Call Flow Builder

CFB is the drag-and-drop visual editor for voice flows. Lives under the Tools tab in the SignalWire portal. Generates valid SWML under the hood, so any flow built in CFB is equivalent to a hand-written script. Useful when non-technical team members need to edit flows, or when the flow is simple enough that visual debugging beats a code review.

Node taxonomy

Starting node (always present)

| Node | Description | |---|---| | Handle Call | Entry point for every incoming call. Cannot be deleted. Cannot be duplicated. |

Action nodes

| Node | Description | |---|---| | Answer Call | Marks the call answered. Optional — most nodes auto-answer when connected after Handle Call. | | Hang Up Call | Disconnects. Reason field is for logs only. | | Play Audio or TTS | Plays audio URL, ringtone (ring:5:jp), silence (silence:5), or TTS. Supports SSML and variables. | | Send SMS | Sends an SMS mid-flow. Fields: To, From, Text. Supports variables. |

Forwarding nodes

| Node | Description | |---|---| | Forward to Phone | Forwards to phone(s) or SIP endpoint(s). Outputs: Success, No Answer, Busy, Decline, Error. Supports sequential or simultaneous dialing. Enable Whisper runs SWML before connecting. |

Recording nodes

| Node | Description | |---|---| | Start Call Recording | Background dual-channel recording. Fields: Name, Stereo, Beep, Terminators, Format. Auto-stops on disconnect. | | Stop Call Recording | Stops a named recording. URL available as %{record_call_url}. | | Voicemail Recording | Async voicemail record. Fields: Stereo, Beep, Terminators, Max Length, Initial/End Silence Timeout, Format. URL via %{record_url}. |

Input nodes

| Node | Description | |---|---| | AI Agent | Connects the call to a SignalWire AI Agent Resource. Single setting: AI Agent Name (dropdown populated from Resources). Handles NLP, sentiment, SWAIG, multi-turn dialogue. | | Gather Input | Collects DTMF and/or speech. Outputs: one connector per input option + Unknown + No Input. %{prompt_value} holds detected speech for the Unknown path. | | Request | HTTP GET/POST/PUT/DELETE. Headers and Body configurable. Response in %{request_response_body} or %{request_response.<field>}. Outputs: user-defined conditions + Else + Failure. |

Decision / data nodes

| Node | Description | |---|---| | Conditions | JavaScript if/else-if logic over %{variable} expressions. JS operators (&&, \|\|, ==, .slice()) work. | | Execute SWML | Fetches and executes a remote SWML document. Passes Params + Meta. Return values via %{return_value.<field>}. | | Set Variables | Creates named variables accessible as %{vars.<key>} for the rest of the flow. | | Unset Variables | Clears previously set variables. |

AI Agent node — what it does

Replaces an entire IVR tree with natural-language handling. Setting: AI Agent Name (dropdown from Resources).

Capabilities the connected agent handles:

  • Natural language processing (free-form speech understanding)
  • Sentiment analysis
  • SWAIG functions (CRM lookup, SMS, transfers)
  • Multi-turn dialogue with context switching

Prerequisites — must exist before the node will work:

  1. Resources → + Add New → AI Agent → Custom AI Agent.
  2. Configure system prompt, persona, SWAIG functions.
  3. Fund the account (AI agents are billed per minute).
  4. The agent name will appear in the AI Agent node dropdown.

Variables reference

All variables use the form %{<variable>}.

Built-in call variables (always available)

| Variable | Description | |---|---| | %{call.from} | Caller's number | | %{call.to} | Number dialed | | %{call.direction} | Currently always inbound | | %{call.call_id} | Unique call ID | | %{call.state} | Current call state | | %{call.type} | Call type |

Request node variables

| Variable | Description | |---|---| | %{request_response_body} | Full response body | | %{request_response.<field>} | Specific JSON field | | %{vars.request_response.<field>} | String field for conditional expressions |

Recording variables

| Variable | Description | |---|---| | %{record_call_url} | URL of stopped Call Recording | | %{record_url} | URL of Voicemail Recording |

Gather Input variables

| Variable | Description | |---|---| | %{prompt_value} | Speech detected on the Unknown output path |

Custom (Set Variables node)

  • Access with %{vars.<key>} or %{<key>}.
  • JS expressions work: %{call.from.slice(2,5)} extracts the area code.
  • String comparisons in conditions: %{vars.request_response.dayOfWeek == 'Thursday'}.

Building a flow

  1. Dashboard → Tools tab → Call Flow Builder.
  2. Add New → name the flow → Save.
  3. More Options → Edit to open the canvas.
  4. The canvas starts with a single Handle Call node — the entry point.
  5. Drag nodes from the left panel.
  6. Connect them: click-drag from output (right side) to input (left side).
  7. Click a node to open its config panel.
  8. Click Deploy to make the flow live.
  9. Assign the flow to a phone number.

Every node has one input on the left and one or more outputs on the right. Multiple outputs = branching logic.

Assigning a flow to a DID

  • Phone Numbers in the Dashboard.
  • Select the number → edit Call Handler.
  • Set handler to Call Flow → select the flow by name.
  • Save.

Flows can also be assigned to SIP Addresses and Domain Applications the same way.

Versioning

  • Version History (top-left of CFB) shows all versions with timestamps.
  • Each Deploy creates a new version (1.0, 2.0, 3.0…).
  • Roll back via Version History → select old version → Restore.
  • Save without deploying to test changes safely.

CFB vs SWML vs Python SDK

| Situation | Use | |---|---| | Simple IVR, AI handoff, voicemail, basic branching | CFB | | Non-technical team members editing flows | CFB | | Visual debugging matters more than code review | CFB | | Complex conditionals, loops, dynamic SWML | SWML directly | | Git-versioned, programmatically generated flows | SWML directly | | Real-time call control, outbound dialing, browser SDK | Python / Node SDK | | Hybrid: visual flow + custom logic | CFB + Execute SWML node pointing at hosted SWML |

Common patterns

Basic AI Agent (simplest)

Handle Call → Answer Call → AI Agent: "My Agent"

IVR with routing

Handle Call → Answer Call → Gather Input (Press 1=Sales, 2=Support)
  → [Sales]   → Forward to Phone: sales_number
  → [Support] → Forward to Phone: support_number
  → [No Input] → Play TTS "No input received" → Hang Up

AI Agent with recording

Handle Call → Answer Call → Start Call Recording → AI Agent: "My Agent"

Voicemail with SMS notification

Handle Call → Answer Call → Play TTS "Leave a message after the beep"
  → Voicemail Recording (beep on, terminator=#)
  → Send SMS: "New voicemail from %{call.from}: %{record_url}"

Business hours routing (API-based)

Handle Call → Answer Call → Request (GET timeapi.io)
  → Conditions: %{request_response.hour} >= 9 && %{request_response.hour} < 17
    → [Open]   → AI Agent or Forward to Phone
    → [Else]   → Play TTS "We're closed" → Voicemail Recording

Loop guard on Gather Input

Handle Call → Answer Call → Set Variables: loop_count = 0
  → Gather Input
    → [No Input] → Set Variables: loop_count = %{loop_count} + 1
                 → Conditions: %{loop_count} >= 2
                   → [True] → Hang Up
                   → [Else] → back to Gather Input

Preserve caller ID on forward

Handle Call → Forward to Phone (From: %{call.from}, To: +15551234567)
  → [Success] → (connected)
  → [No Answer / Busy / Decline / Error] → Hang Up

Multi-request flow (variable persistence)

Handle Call → Answer Call → Request: API #1
  → Set Variables: my_data = %{request_response.field}
  → Request: API #2   (overwrites %{request_response}; %{vars.my_data} still readable)
  → Play TTS: "Result: %{vars.my_data}"

Anti-patterns

  • Not creating the AI Agent Resource before adding the AI Agent node — the dropdown will be empty.
  • Removing Handle Call — it can't be removed. Stop trying.
  • Treating Answer Call as mandatory — it's optional; other nodes auto-answer.
  • Using %{request_response.<field>} after a second Request node — the second call overwrites the variable. Persist with Set Variables.
  • Wiring a Gather Input No Input output back to itself with no counter — infinite loop.
  • Deploying without testing — save first, then Deploy after verification.
  • Pointing Execute SWML at a URL that returns HTML — it must return JSON or YAML.
  • Assigning a flow to a number before deploying — the flow won't be selectable until deployed.

See also