Call legs
A forwarded call is one conversation to the people on it. To SignalWire it is two legs: the caller’s connection to your number, and the connection SignalWire dials to the destination. Each leg has its own Call ID, its own log record, its own status callbacks, and its own charge. Once you can see the legs in a call, the second log entry, the extra callback, and the unfamiliar ID in a webhook all make sense.
This page explains what a leg is, how legs come into being, how to tell which leg you’re looking at, and what you can do to one leg without touching the other.
What a call leg is
A call leg is the connection between two user agents. A user agent is anything that can hold up its end of a call: a phone on the public network, a SIP device or PBX, a browser running the Browser SDK, or SignalWire itself. A call is everything the person on the other end experiences, from the moment it is placed to the moment it ends, and it is made of one or more legs.
The leg that started the call is the A-leg. A leg SignalWire dials on its behalf is a B-leg. Forward an inbound call to one SIP endpoint and the call has two legs. Ring four endpoints at once and it has five, because every destination SignalWire rings is a leg, including the ones that never answer.
The Browser SDK also uses the word leg for the connections inside one browser call: the main leg, the screen-share leg, and the additional-device leg. Each of those auxiliary legs opens its own connection to SignalWire with its own call ID.
How legs are created
Every leg starts the same way: someone, or something, dials. The table lists what adds legs to a call and how many.
Forward calls walks through the two-leg case with code, and Make and receive calls covers the single-leg start.
What each leg keeps
Each leg carries its own state: a direction, a from and to, a call state with its own start, answer,
and end times, and its own log record. On a B-leg, from is the caller ID you chose and to is the
destination, so the same call looks different depending on which leg reports it.
In SWML, each leg has its own call object and its own variables.
SWML variables live on the leg that set them, for as long as that leg lasts. A document that runs on the leg connect dialed, such as a confirm script, starts with an empty vars and its own call object. On the leg that issued connect, every variable survives, and connect_result is set when the document resumes. For how legs relate, see Call legs.
The AI agent’s global_data follows the same rule. It persists across AI sessions on one leg through
the ai_agents_global_data variable, as the AI sidecar reference describes, and it
does not cross to another leg.
Tell legs apart
Every surface identifies legs with the same handful of fields under slightly different names. The glossary lists all of them side by side. The ones below relate legs to each other.
A Call ID names one leg for its whole life, and every leg in a call has a different one. A Segment ID
names the leg’s log record, and it is the value to quote to Support. A leg’s first segment reuses its
Call ID, so the two match for most calls. When SignalWire re-routes a leg to a new handler, a new
segment starts on the same leg: the Segment ID changes and the Call ID does not. That happens when a
SIP device sends SignalWire a REFER, and when cXML transfers the call. The Compatibility API calls the
Segment ID CallSid, and the Segment IDs page shows where to find it in the Dashboard.
The parent is the leg that created this one. SWML webhooks and status callbacks send it as parent,
the REST voice logs and Calling API as parent_id, and the Compatibility API as ParentCallSid. The
peer is the leg this one is connected to. It arrives as peer in connect status callbacks and in Relay
connect events. A dialed leg has its peer from the moment it’s created, so a confirm script can read
it. SignalWire clears it when the bridge ends, so a SWML document that resumes after connect has no
call.peer.
Two more fields say which leg won and which legs belong together. The dial_winner flag marks the leg
that answered a Relay or Calling API dial, including the winning device of a parallel dial. Legs that
a connect dials don’t carry it. A tag is a label you set on a Relay dial or connect request. Every
leg that request creates shares it, and SignalWire echoes it on those legs’ own events and as peer.tag
in the connected event of the leg that issued the connect.
Here is the call object a confirm script receives on the B-leg of a connect, abridged to the fields
above. The leg is outbound, its segment_id still matches its call_id, and both its parent and its
peer are the A-leg:
The full field list is in the inbound call webhook reference.
Where legs show up
Act on one leg or both
Because the legs are separate, what happens when one ends depends on which one it is. When the dialed
leg hangs up, the leg that connected it carries on: a SWML document continues past its connect, and
by default a Relay handler receives the disconnected event. When the leg that issued connect hangs up,
SignalWire hangs up the dialed leg too, because nothing else controls it. A connect to a queue is the
exception. Once the agent and the caller are bridged, the agent’s document doesn’t resume, and
execute_after_queue decides what the agent’s leg does next.
To break the bridge and keep this leg, send the REST Calling API’s calling.disconnect
command or the Relay disconnect method to the leg that issued connect. Your code can
then play a message or connect the caller somewhere else. The other leg doesn’t hang up: it stays up and
parked, and no event fires for it, so hang it up or connect it yourself.
Two operations change a call without adding a leg. SIP REFER asks the SIP device to place
the next call itself. When it succeeds, your app hangs up SignalWire’s leg and the call continues
without SignalWire. SWML transfer moves the same leg to a new document, and nothing
about the leg changes except what runs on it.
Legs and billing
Each leg is billed on its own, and each voice log entry carries its own charge and charge_details.
A forwarded call therefore produces two entries: the caller’s inbound leg to your number, and the
outbound leg SignalWire dialed to the destination. A parallel dial produces an entry for every
destination SignalWire dialed, including the ones that never answered.