{"slug":"swml-connect-verb","title":"SWML connect — Bridge to PSTN, SIP, or AI Platforms","tags":["signalwire","swml","connect","sip","byo-trunk"],"agent_summary":"The `connect` verb bridges the active call to a destination — PSTN, SIP URI, or AI platform endpoint. Covers basic forwarding, simultaneous and sequential dialing, whisper, answer_on_bridge, and SIP URI patterns for BYO trunks.","trigger_phrases":["SWML connect verb","bridge call to PSTN","forward call SIP","ring multiple numbers","answer_on_bridge","whisper before connect","BYO trunk SIP URI"],"runnable":true,"markdown":"\n# SWML `connect` Verb\n\n`connect` bridges the active call to one or more destinations. Destinations can be PSTN numbers (E.164), SIP URIs, or a mix. This is the verb that powers call forwarding, hunt groups, ring-all groups, and every BYO SIP integration.\n\n## Simplest case — forward to a phone\n\n```yaml\nversion: 1.0.0\nsections:\n  main:\n    - connect:\n        to: \"+15555551234\"\n```\n\nInbound call is answered (implicitly) and bridged to `+15555551234`. The caller stays on the line until the destination hangs up.\n\n## Preserving caller ID\n\nBy default the inbound caller's number is forwarded. To override (for example to spoof your own DID), set `from`:\n\n```yaml\n- connect:\n    from: \"+15559876543\"\n    to: \"+15555551234\"\n```\n\nCaller ID rules apply: `from` must be a verified or owned number on your project.\n\n## Sequential dialing (hunt group)\n\nPass an array of destinations. The list is dialed in order, top to bottom, until one answers.\n\n```yaml\n- connect:\n    to:\n      - \"+15555550101\"\n      - \"+15555550102\"\n      - \"+15555550103\"\n    timeout: 20   # seconds per destination\n```\n\nTotal ring time is `timeout * len(to)`. Use a short `timeout` (15-20s) to avoid making the caller wait through three full timeouts.\n\n## Simultaneous dialing (ring all)\n\nWrap each leg in a `[ ... ]` inner array. All inner items ring at the same time; first to answer wins.\n\n```yaml\n- connect:\n    to:\n      - [\"+15555550101\", \"+15555550102\", \"+15555550103\"]\n    timeout: 25\n```\n\nThe outer list is sequential, the inner list is parallel. So `[[a, b], c]` means \"ring a and b together; if neither answers in `timeout`, ring c.\"\n\n## SIP URI destinations\n\nA SIP destination uses the `sip:` prefix.\n\n```yaml\n- connect:\n    to: \"sip:agent42@your-pbx.example.com\"\n```\n\nAdd `;transport=tcp` or `;transport=tls` when required by the receiving endpoint.\n\n```yaml\n- connect:\n    to: \"sip:%{call.to}@your-livekit-domain.com;transport=tcp\"\n```\n\n`%{call.to}` substitutes the dialed DID so the receiving platform can route by called-number.\n\n## `answer_on_bridge` — delay answer until callee picks up\n\nDefault behaviour is to answer the inbound call immediately. With `answer_on_bridge: true`, SignalWire keeps the inbound leg ringing on the caller's side until the callee answers, then bridges. The caller hears ringing instead of dead air, and billing starts only when the callee picks up.\n\n```yaml\n- connect:\n    answer_on_bridge: true\n    from: \"+15559876543\"\n    to: \"+15555551234\"\n```\n\nUse it for any plain forwarding scenario. Required for clean outbound-from-AI-platform setups where the receiving side expects ringback.\n\n## Whisper — play audio to the callee before they hear the caller\n\n`whisper` makes SignalWire play a private prompt or SWML script to the answering party before the bridge completes. Useful for screening, recording warnings, or announcing the source of the call.\n\n```yaml\n- connect:\n    to: \"+15555551234\"\n    whisper:\n      url: \"say:Incoming call from a new lead. Press 1 to accept.\"\n```\n\n`whisper.url` can be a hosted audio file, a `say:` TTS string, or another SWML document URL.\n\n## `confirm` — secondary SWML before bridging\n\n`confirm` runs a separate SWML doc against the callee leg before the bridge. Common pattern: start recording, log to your CRM, or whisper a prompt.\n\n```yaml\n# record-helper.yaml\nversion: 1.0.0\nsections:\n  main:\n    - record_call: { stereo: true, format: mp3 }\n\n# main.yaml\nversion: 1.0.0\nsections:\n  main:\n    - connect:\n        answer_on_bridge: true\n        from: \"+15559876543\"\n        to: \"+15555551234\"\n        confirm: \"https://your.api/record-helper.yaml\"\n```\n\n## BYO-trunk patterns\n\nTwo patterns dominate BYO integration setups (VAPI, LiveKit, Bland, Retell):\n\n### Inbound: PSTN → AI platform\n\n```yaml\nversion: 1.0.0\nsections:\n  main:\n    - connect:\n        to: \"sip:%{call.to}@your-credential-id.sip.vapi.ai\"\n```\n\n### Outbound: AI platform → PSTN (assigned to a SIP Address)\n\n```yaml\nversion: 1.0.0\nsections:\n  main:\n    - connect:\n        answer_on_bridge: true\n        from: \"+15555551234\"\n        to: \"%{call.to.replace(/^sip:/i, '').replace(/@.*/, '')}\"\n```\n\nThe `to` expression strips the `sip:` prefix and `@domain` suffix so the value is clean E.164. Full BYO setup details: [VAPI BYO](/topic/byo-sip-trunk-vapi) and [LiveKit BYO](/topic/byo-sip-trunk-livekit).\n\n## `connect` parameters reference\n\n| Parameter | Notes |\n|---|---|\n| `to` | string or array — destination(s). Required. |\n| `from` | string — override outbound caller ID. |\n| `timeout` | int — seconds to ring each destination. Default 30. |\n| `answer_on_bridge` | bool — keep ringback to inbound caller until bridge. |\n| `confirm` | string — URL of secondary SWML to run against callee. |\n| `whisper` | object — `url` (audio/say:/SWML URL) and optional `terminators`. |\n| `headers` | object — custom SIP headers (SIP destinations only). |\n| `codecs` | string — comma-separated codec preference list (G.722, G.711, Opus). |\n| `webrtc_media` | bool — force WebRTC media path. |\n| `session_timeout` | int — max bridge duration in seconds. |\n\n## Anti-patterns\n\n- Forgetting `answer_on_bridge: true` on outbound BYO trunk scripts — caller hears dead air.\n- Hardcoding internal IPs as SIP destinations — always use a hostname so SignalWire's resolver finds the right edge.\n- Long `timeout` (60s+) on sequential dialing — multiplies the caller's wait.\n- Using one giant SIP URI string instead of headers — custom headers go in `headers`, not appended to the URI.\n- Connecting to a SIP URI without `;transport=tcp` when the receiver requires it (LiveKit inbound especially).\n\n## See also\n\n- [VAPI BYO SIP trunk](/topic/byo-sip-trunk-vapi)\n- [LiveKit BYO SIP trunk](/topic/byo-sip-trunk-livekit)\n- [SWML overview](/topic/swml-overview)\n- [Call Flow Builder — Forward to Phone node](/topic/signalwire-call-flow-builder)\n","html":"<h1>SWML <code>connect</code> Verb</h1>\n<p><code>connect</code> bridges the active call to one or more destinations. Destinations can be PSTN numbers (E.164), SIP URIs, or a mix. This is the verb that powers call forwarding, hunt groups, ring-all groups, and every BYO SIP integration.</p>\n<h2>Simplest case — forward to a phone</h2>\n<pre><code class=\"language-yaml\">version: 1.0.0\nsections:\n  main:\n    - connect:\n        to: \"+15555551234\"\n</code></pre>\n<p>Inbound call is answered (implicitly) and bridged to <code>+15555551234</code>. The caller stays on the line until the destination hangs up.</p>\n<h2>Preserving caller ID</h2>\n<p>By default the inbound caller's number is forwarded. To override (for example to spoof your own DID), set <code>from</code>:</p>\n<pre><code class=\"language-yaml\">- connect:\n    from: \"+15559876543\"\n    to: \"+15555551234\"\n</code></pre>\n<p>Caller ID rules apply: <code>from</code> must be a verified or owned number on your project.</p>\n<h2>Sequential dialing (hunt group)</h2>\n<p>Pass an array of destinations. The list is dialed in order, top to bottom, until one answers.</p>\n<pre><code class=\"language-yaml\">- connect:\n    to:\n      - \"+15555550101\"\n      - \"+15555550102\"\n      - \"+15555550103\"\n    timeout: 20   # seconds per destination\n</code></pre>\n<p>Total ring time is <code>timeout * len(to)</code>. Use a short <code>timeout</code> (15-20s) to avoid making the caller wait through three full timeouts.</p>\n<h2>Simultaneous dialing (ring all)</h2>\n<p>Wrap each leg in a <code>[ ... ]</code> inner array. All inner items ring at the same time; first to answer wins.</p>\n<pre><code class=\"language-yaml\">- connect:\n    to:\n      - [\"+15555550101\", \"+15555550102\", \"+15555550103\"]\n    timeout: 25\n</code></pre>\n<p>The outer list is sequential, the inner list is parallel. So <code>[[a, b], c]</code> means \"ring a and b together; if neither answers in <code>timeout</code>, ring c.\"</p>\n<h2>SIP URI destinations</h2>\n<p>A SIP destination uses the <code>sip:</code> prefix.</p>\n<pre><code class=\"language-yaml\">- connect:\n    to: \"sip:agent42@your-pbx.example.com\"\n</code></pre>\n<p>Add <code>;transport=tcp</code> or <code>;transport=tls</code> when required by the receiving endpoint.</p>\n<pre><code class=\"language-yaml\">- connect:\n    to: \"sip:%{call.to}@your-livekit-domain.com;transport=tcp\"\n</code></pre>\n<p><code>%{call.to}</code> substitutes the dialed DID so the receiving platform can route by called-number.</p>\n<h2><code>answer_on_bridge</code> — delay answer until callee picks up</h2>\n<p>Default behaviour is to answer the inbound call immediately. With <code>answer_on_bridge: true</code>, SignalWire keeps the inbound leg ringing on the caller's side until the callee answers, then bridges. The caller hears ringing instead of dead air, and billing starts only when the callee picks up.</p>\n<pre><code class=\"language-yaml\">- connect:\n    answer_on_bridge: true\n    from: \"+15559876543\"\n    to: \"+15555551234\"\n</code></pre>\n<p>Use it for any plain forwarding scenario. Required for clean outbound-from-AI-platform setups where the receiving side expects ringback.</p>\n<h2>Whisper — play audio to the callee before they hear the caller</h2>\n<p><code>whisper</code> makes SignalWire play a private prompt or SWML script to the answering party before the bridge completes. Useful for screening, recording warnings, or announcing the source of the call.</p>\n<pre><code class=\"language-yaml\">- connect:\n    to: \"+15555551234\"\n    whisper:\n      url: \"say:Incoming call from a new lead. Press 1 to accept.\"\n</code></pre>\n<p><code>whisper.url</code> can be a hosted audio file, a <code>say:</code> TTS string, or another SWML document URL.</p>\n<h2><code>confirm</code> — secondary SWML before bridging</h2>\n<p><code>confirm</code> runs a separate SWML doc against the callee leg before the bridge. Common pattern: start recording, log to your CRM, or whisper a prompt.</p>\n<pre><code class=\"language-yaml\"># record-helper.yaml\nversion: 1.0.0\nsections:\n  main:\n    - record_call: { stereo: true, format: mp3 }\n\n# main.yaml\nversion: 1.0.0\nsections:\n  main:\n    - connect:\n        answer_on_bridge: true\n        from: \"+15559876543\"\n        to: \"+15555551234\"\n        confirm: \"https://your.api/record-helper.yaml\"\n</code></pre>\n<h2>BYO-trunk patterns</h2>\n<p>Two patterns dominate BYO integration setups (VAPI, LiveKit, Bland, Retell):</p>\n<h3>Inbound: PSTN → AI platform</h3>\n<pre><code class=\"language-yaml\">version: 1.0.0\nsections:\n  main:\n    - connect:\n        to: \"sip:%{call.to}@your-credential-id.sip.vapi.ai\"\n</code></pre>\n<h3>Outbound: AI platform → PSTN (assigned to a SIP Address)</h3>\n<pre><code class=\"language-yaml\">version: 1.0.0\nsections:\n  main:\n    - connect:\n        answer_on_bridge: true\n        from: \"+15555551234\"\n        to: \"%{call.to.replace(/^sip:/i, '').replace(/@.*/, '')}\"\n</code></pre>\n<p>The <code>to</code> expression strips the <code>sip:</code> prefix and <code>@domain</code> suffix so the value is clean E.164. Full BYO setup details: <a href=\"/topic/byo-sip-trunk-vapi\">VAPI BYO</a> and <a href=\"/topic/byo-sip-trunk-livekit\">LiveKit BYO</a>.</p>\n<h2><code>connect</code> parameters reference</h2>\n<p>| Parameter | Notes |\n|---|---|\n| <code>to</code> | string or array — destination(s). Required. |\n| <code>from</code> | string — override outbound caller ID. |\n| <code>timeout</code> | int — seconds to ring each destination. Default 30. |\n| <code>answer_on_bridge</code> | bool — keep ringback to inbound caller until bridge. |\n| <code>confirm</code> | string — URL of secondary SWML to run against callee. |\n| <code>whisper</code> | object — <code>url</code> (audio/say:/SWML URL) and optional <code>terminators</code>. |\n| <code>headers</code> | object — custom SIP headers (SIP destinations only). |\n| <code>codecs</code> | string — comma-separated codec preference list (G.722, G.711, Opus). |\n| <code>webrtc_media</code> | bool — force WebRTC media path. |\n| <code>session_timeout</code> | int — max bridge duration in seconds. |</p>\n<h2>Anti-patterns</h2>\n<ul>\n<li>Forgetting <code>answer_on_bridge: true</code> on outbound BYO trunk scripts — caller hears dead air.</li>\n<li>Hardcoding internal IPs as SIP destinations — always use a hostname so SignalWire's resolver finds the right edge.</li>\n<li>Long <code>timeout</code> (60s+) on sequential dialing — multiplies the caller's wait.</li>\n<li>Using one giant SIP URI string instead of headers — custom headers go in <code>headers</code>, not appended to the URI.</li>\n<li>Connecting to a SIP URI without <code>;transport=tcp</code> when the receiver requires it (LiveKit inbound especially).</li>\n</ul>\n<h2>See also</h2>\n<ul>\n<li><a href=\"/topic/byo-sip-trunk-vapi\">VAPI BYO SIP trunk</a></li>\n<li><a href=\"/topic/byo-sip-trunk-livekit\">LiveKit BYO SIP trunk</a></li>\n<li><a href=\"/topic/swml-overview\">SWML overview</a></li>\n<li><a href=\"/topic/signalwire-call-flow-builder\">Call Flow Builder — Forward to Phone node</a></li>\n</ul>\n"}