9.1 The dialog ID: Call-ID and two tags
A transaction lasts seconds (Module 8). A call lasts minutes, and many transactions happen during it: the INVITE, a re-INVITE for hold, an INFO for a key press, the BYE. The dialog ties them together:
“A dialog represents a peer-to-peer SIP relationship between two user agents that persists for some time.”Read the section ↗
Each side names the dialog with three values: the Call-ID, its own tag (the local tag), and the other side’s tag (the remote tag). The two sides see the same values, with the tags swapped:
“A dialog is identified at each UA with a dialog ID, which consists of a Call-ID value, a local tag and a remote tag. The dialog ID at each UA involved in the dialog is not the same. Specifically, the local tag at one UA is identical to the remote tag at the peer UA.”Read the section ↗
| Alice (she called) | Bob (he answered) | |
|---|---|---|
| Call-ID | 3848276298220188511@192.0.2.10 |
3848276298220188511@192.0.2.10 |
| Local tag | 9fxced76sl — her From tag |
314159 — his To tag |
| Remote tag | 314159 — the To tag |
9fxced76sl — the From tag |
In every request, From carries the sender’s tag and To the receiver’s. So in Alice’s requests, From has 9fxced76sl; in Bob’s requests — a re-INVITE, or a BYE when he hangs up — From has 314159 (Module 7).
9.2 Early dialogs and confirmed dialogs
A response with a To tag creates a dialog — but only some responses:
“Within this specification, only 2xx and 101-199 responses with a To tag, where the request was INVITE, will establish a dialog.”Read the section ↗
- A 180 Ringing or 183 Session Progress with a To tag creates an early dialog. It already has state, so requests such as PRACK and UPDATE can travel in it before the call is answered.
- A 2xx creates a confirmed dialog, or confirms the early dialog with the same tags.
- A 100 Trying never creates a dialog. It usually has no To tag, and it stays on one hop.
“A dialog can also be in the "early" state, which occurs when it is created with a provisional response, and then transition to the "confirmed" state when a 2xx final response arrives. For other responses, or if no response arrives at all on that dialog, the early dialog terminates.”Read the section ↗
9.3 What each side remembers
“This state consists of the dialog ID, a local sequence number (used to order requests from the UA to its peer), a remote sequence number (used to order requests from its peer to the UA), a local URI, a remote URI, remote target, a boolean flag called "secure", and a route set, which is an ordered list of URIs.”Read the section ↗
| Field | The caller (UAC) takes it from | The callee (UAS) takes it from |
|---|---|---|
| Local CSeq | The CSeq of its INVITE | Empty, until it sends its first request |
| Remote CSeq | Empty, until the callee sends a request | The CSeq of the INVITE |
| Local URI / remote URI | From / To of the INVITE | To / From of the INVITE |
| Remote target | Contact of the response | Contact of the INVITE |
| Route set | Record-Route of the response, reversed | Record-Route of the request, in order |
“The route set MUST be set to the list of URIs in the Record-Route header field from the response, taken in reverse order and preserving all URI parameters.”Read the section ↗
“The route set MUST be set to the list of URIs in the Record-Route header field from the request, taken in order and preserving all URI parameters.”Read the section ↗
Each side counts its own requests. Alice’s CSeq goes 1 (INVITE), 2 (BYE); Bob starts wherever he likes — 4711 in the example — and adds one from there. ACK and CANCEL reuse the number of the request they belong to:
“Requests within a dialog MUST contain strictly monotonically increasing and contiguous CSeq sequence numbers (increasing-by-one) in each direction (excepting ACK and CANCEL of course, whose numbers equal the requests being acknowledged or cancelled).”Read the section ↗
The remote target changes only with a target refresh request — a re-INVITE or an UPDATE — or the 2xx to one. That is how a phone that changes its address keeps the call:
“Specifically, requests that are not target refresh requests do not modify the dialog's remote target URI, and requests that are target refresh requests do.”Read the section ↗
Step through the call below. Watch both tables: the dialog appears with the 180, becomes confirmed with the 200 OK, gets a new remote target from Bob’s re-INVITE, and ends with the BYE. The panel under the player shows the four layers that each message belongs to (9.8).
Live dialog table
One dialog, from INVITE to BYE
- +New dialog
- ~Changed at this step
Alice sends the INVITE. Her From tag is the first half of the dialog ID.
- Call
- Call-ID
3848276298220188511@192.0.2.10 - Session
- SDP
o=alice 2890844526 2890844526 IN IP4 192.0.2.10 - Dialog
- From tag
9fxced76sl· To tag— (none yet) - Transaction
- branch
z9hG4bK776asdhds· INVITE
Alice0 dialogs
No dialog yet. A dialog starts with a 101–199 response that has a To tag, or with a 2xx.
Bob0 dialogs
No dialog yet. A dialog starts with a 101–199 response that has a To tag, or with a 2xx.
9.4 Requests inside a dialog
Every request inside a dialog is built from the dialog state, not from the first INVITE:
- To and From: the remote and local URI, with the remote and local tag.
- Call-ID: the dialog’s Call-ID.
- CSeq: the local CSeq plus one.
- Request-URI: the remote target.
- Route: the route set, in order.
“If the route set is not empty, and the first URI in the route set contains the lr parameter (see Section 19.1.1), the UAC MUST place the remote target URI into the Request-URI and MUST include a Route header field containing the route set values in order, including all parameters.”Read the section ↗
| Request | In a dialog it… |
|---|---|
| re-INVITE | Changes the session (hold, codec, video) with a new offer. A target refresh. |
| UPDATE | Changes the session, even before the answer (RFC 3311). A target refresh. |
| BYE | Ends the session and the dialog. |
| INFO | Carries application data, such as a key press (RFC 6086). |
| REFER | Asks the other side to call someone (transfer, Module 22). |
| NOTIFY | Reports the progress of a REFER, in the same dialog. |
| PRACK | Acknowledges a reliable provisional response, in the early dialog (RFC 3262). |
| ACK | Acknowledges a 2xx to INVITE or re-INVITE. Not a target refresh. |
A re-INVITE goes to one device — the remote target — so it never forks:
“Unlike an INVITE, which can fork, a re-INVITE will never fork, and therefore, only ever generate a single final response.”Read the section ↗
A request with tags that match no dialog gets 481 Call/Transaction Does Not Exist:
“If the UAS wishes to reject the request because it does not wish to recreate the dialog, it MUST respond to the request with a 481 (Call/Transaction Does Not Exist) status code and pass that to the server transaction.”Read the section ↗
“If the response for a request within a dialog is a 481 (Call/Transaction Does Not Exist) or a 408 (Request Timeout), the UAC SHOULD terminate the dialog.”Read the section ↗
9.5 Forking: many early dialogs, and more than one 2xx
When Proxy B forks an INVITE to Bob’s three devices (Module 3), each device that rings adds its own To tag. Alice gets one early dialog per tag — all with the same Call-ID and the same local tag. If two devices answer at once, both 2xx responses reach her:
“Multiple 2xx responses may arrive at the UAC for a single INVITE request due to a forking proxy. Each response is distinguished by the tag parameter in the To header field, and each represents a distinct dialog, with a distinct dialog identifier.”Read the section ↗
Alice must ACK every 2xx. If she wants only one call, she then ends the others with BYE:
“If, after acknowledging any 2xx response to an INVITE, the UAC does not want to continue with that dialog, then the UAC MUST terminate the dialog by sending a BYE request as described in Section 15.”Read the section ↗
The early dialogs that never got a 2xx do not end at once. Alice waits 64×T1 after the first 2xx, in case another 2xx is on its way:
“The UAC core considers the INVITE transaction completed 64*T1 seconds after the reception of the first 2xx response. At this point all the early dialogs that have not transitioned to established dialogs are terminated.”Read the section ↗
Forking tree
Forking: one phone answers
Alice calls bob@biloxi.example.
Alice’s dialogs
No dialog yet: no response with a To tag has reached Alice.
9.6 Glare and 491 Request Pending
A dialog allows only one INVITE transaction at a time, in either direction:
“Note that a UAC MUST NOT initiate a new INVITE transaction within a dialog while another INVITE transaction is in progress in either direction.”Read the section ↗
When both sides send a re-INVITE at the same moment — glare — each side has its own INVITE in progress, so each rejects the other’s:
“A UAS that receives an INVITE on a dialog while an INVITE it had sent on that dialog is in progress MUST return a 491 (Request Pending) response to the received INVITE.”Read the section ↗
Both then wait a random time and try again. The side that created the Call-ID waits longer (2.1 to 4 seconds); the other side waits 0 to 2 seconds, so it usually goes first:
“If the UAC is the owner of the Call-ID of the dialog ID (meaning it generated the value), T has a randomly chosen value between 2.1 and 4 seconds in units of 10 ms.”Read the section ↗
Call flow · two re-INVITEs cross
Glare: two re-INVITEs cross
- SIP
Alice puts the call on hold with a re-INVITE. Her SDP says sendonly.
All steps as text
- Alice → Bob: INVITE (hold). Alice puts the call on hold with a re-INVITE. Her SDP says sendonly.
- Bob → Alice: INVITE (add video). At the same moment, Bob adds video with his own re-INVITE.
- Bob → Alice: 491 Request Pending. Bob has a re-INVITE of his own in progress, so he rejects Alice's with 491.
- Alice → Bob: ACK. Alice ACKs the 491. Her session stays as it was.
- Alice → Bob: 491 Request Pending. Alice has the same problem, so she rejects Bob's re-INVITE with 491 too.
- Bob → Alice: ACK. Bob ACKs the 491.
- Bob → Alice: INVITE (add video). Bob does not own the Call-ID, so he waits 0 to 2 seconds. After 1.3 s he tries again.
- Alice → Bob: 200 OK. Nothing crosses this time. Alice accepts the video.
- Bob → Alice: ACK. Bob ACKs the 200 OK. The call now has video.
- Alice → Bob: INVITE (hold). Alice owns the Call-ID, so she waits 2.1 to 4 seconds. After 3.2 s she tries again.
- Bob → Alice: 200 OK. Bob accepts the hold.
- Alice → Bob: ACK. Alice ACKs the 200 OK. Both changes are now done, one after the other.
9.7 Session timers: who refreshes, and how
Module 7 introduced Session-Expires and Min-SE. In the dialog, both sides run a timer for the session interval, and one side — the refresher — must send a refresh before it runs out:
“The Session-Expires header field also contains a 'refresher' parameter, which indicates who is doing the refreshing -- the UA that is currently the UAC, or the UA that is currently the UAS.”Read the section ↗
The refresher parameter says uac or uas — relative to the current transaction. When Bob, who answered the call, sends the refresh UPDATE, he is the UAC of that UPDATE, so it says refresher=uac.
“It is RECOMMENDED that this refresh be sent once half the session interval has elapsed.”Read the section ↗
A refresh is a re-INVITE or an UPDATE; only its 2xx restarts the timer. If no refresh arrives, the other side ends the call slightly before the session expires:
“Similarly, if the side not performing refreshes does not receive a session refresh request before the session expiration, it SHOULD send a BYE to terminate the session, slightly before the session expiration. The minimum of 32 seconds and one third of the session interval is RECOMMENDED.”Read the section ↗
Call flow · session timer refreshes
Session timer: Bob refreshes
- SIP
Alice supports session timers and asks for an interval of 1800 s (30 minutes).
All steps as text
- Alice → Bob: INVITE. Alice supports session timers and asks for an interval of 1800 s (30 minutes).
- Bob → Alice: 200 OK. Bob accepts 1800 s and chooses himself, the UAS, as the refresher.
- Alice → Bob: ACK. Alice ACKs. Both phones start a 1800 s session timer.
- Bob → Alice: UPDATE (refresh). At 900 s, half the interval, Bob refreshes. In this UPDATE Bob is the UAC, so refresher=uac.
- Alice → Bob: 200 OK. The 200 OK restarts the timer: the session now lasts until 2700 s.
- Bob → Alice: UPDATE (refresh). At 1800 s, Bob refreshes again.
- Alice → Bob: 200 OK. The session now lasts until 3600 s.
- Alice → Bob: BYE. At 2400 s, Alice hangs up.
- Bob → Alice: 200 OK. Bob ends the call.
“Note that the session timer refreshes the session, not the dialog used to establish the session.”Read the section ↗
9.8 Call, session, dialog, transaction
Four words that are easy to mix up:
| What it is | Named by | How many in one simple call | |
|---|---|---|---|
| Call | Everything that belongs to one Call-ID | Call-ID | 1 |
| Session | The media exchange: audio, video | SDP o= line (Module 15) |
1 |
| Dialog | The SIP relationship between two UAs | Call-ID + both tags | 1 — or more after forking |
| Transaction | One request and its responses | Top Via branch + method | One per request: INVITE, ACK for 2xx, BYE… on each hop |
They do not always line up:
- A forked INVITE is one call with several dialogs (9.5).
- A B2BUA joins two dialogs, often with two Call-IDs, into one conversation (Module 3).
- A session timer refreshes the session; a BYE ends the session and the dialog together.
- A re-INVITE is a new transaction in the same dialog, and it changes the same session.
Common mistakes
Sending an in-dialog request to the first Request-URI
The first INVITE went to an address of record, a group, or a phone number. Requests inside the dialog must go to the remote target — the Contact — through the route set. A phone that reuses the first Request-URI sends its BYE back into routing, where it can reach another device, or no device at all.
Broken
Broken: BYE to the first Request-URI
- SIP
- ⚠Problem
Alice calls the support line: sip:support@biloxi.example.
All steps as text
- Alice → Proxy B: INVITE. Alice calls the support line: sip:support@biloxi.example.
- Proxy B → Alice: 100 Trying. Proxy B answers 100 Trying on this hop.
- Proxy B → Bob: INVITE. Proxy B picks a free agent, Bob. It does not record-route.
- Bob → Proxy B: 180 Ringing. Bob's phone rings.
- Proxy B → Alice: 180 Ringing. Proxy B forwards the 180.
- Bob → Proxy B: 200 OK. Bob answers. His Contact is sip:bob@203.0.113.20.
- Proxy B → Alice: 200 OK. The route set is empty, and the remote target is Bob's Contact.
- Alice → Bob: ACK. Alice sends the ACK straight to the remote target.
- Alice → Proxy B: BYE. Alice hangs up. Her phone sends the BYE to sip:support@biloxi.example, the first Request-URI. Problem: The Request-URI must be the remote target from the Contact, not the first Request-URI.
- Proxy B → Carol: BYE. Proxy B routes the BYE like a new request to the support line. The next free agent is Carol.
- Carol → Proxy B: 481 Call Does Not Exist. Carol's phone has no dialog with these tags. It answers 481.
- Proxy B → Alice: 481 Call Does Not Exist. Alice's phone ends its dialog. Bob's phone still shows a call with nobody on it. Problem: Bob never receives the BYE.
481 Call/Transaction Does Not Exist
The receiver has no dialog with these tags. The usual causes:
- An element restarted or failed over, and lost its dialog state.
- A B2BUA or SBC changed the tags on one side, and a request reached the wrong leg.
- The sender used the wrong tags, or swapped them (Module 7).
- The request went to the wrong device (see the mistake above).
Remember: after a 481 (or a 408) to a request inside a dialog, the sender ends the dialog. Check which element answered 481, and compare its tags with the 200 OK that created the dialog.
Not ACKing and ending the extra 2xx after forking
Two devices answer at the same moment. A caller that ACKs only the first 2xx leaves the second device in a call with nobody: it sends its 200 OK for 32 seconds, then gives up with BYE. The caller must ACK every 2xx, then BYE the dialogs it does not want.
Broken
Broken: Alice ignores the second 200 OK
- SIP
- ⚠Problem
Alice calls bob@biloxi.example.
All steps as text
- Alice → Proxy B: INVITE. Alice calls bob@biloxi.example.
- Proxy B → Alice: 100 Trying. Proxy B answers 100 Trying on this hop.
- Proxy B → Desk phone: INVITE. Bob has 2 registered devices. Proxy B forks the INVITE to the desk phone, with its own branch.
- Proxy B → Mobile: INVITE. Proxy B also sends the INVITE to the mobile.
- Desk phone → Proxy B: 180 Ringing. The desk phone rings. It picks its own To tag: d7c1.
- Proxy B → Alice: 180 Ringing. Alice now has 1 early dialog, one for each To tag.
- Mobile → Proxy B: 180 Ringing. The mobile rings. It picks its own To tag: m2a8.
- Proxy B → Alice: 180 Ringing. Alice now has 2 early dialogs, one for each To tag.
- Desk phone → Proxy B: 200 OK. Bob answers on the desk phone.
- Proxy B → Alice: 200 OK. Proxy B forwards the 200 OK. Alice's dialog with the desk phone becomes confirmed.
- Mobile → Proxy B: 200 OK. At the same moment, someone answers the mobile too.
- Proxy B → Alice: 200 OK. Proxy B forwards every 2xx. Alice now has two confirmed dialogs.
- Alice → Proxy B: ACK. Alice ACKs the desk phone's 200 OK.
- Proxy B → Desk phone: ACK. Proxy B forwards the ACK to the desk phone.
- Mobile → Proxy B: 200 OK (again). No ACK reaches the mobile. It sends its 200 OK again, and again.
- Proxy B → Alice: 200 OK (again). Alice ignores the second 200 OK. She does not ACK it and does not end it. Problem: Every 2xx needs an ACK, even from a dialog the caller does not want.
- Mobile → Proxy B: BYE. After 64×T1 (32 s) with no ACK, the mobile gives up with BYE. For 32 s, it showed a call that nobody was on.
- Proxy B → Alice: BYE. Proxy B forwards the BYE to Alice.
- Alice → Proxy B: 200 OK. Alice answers the BYE.
- Proxy B → Mobile: 200 OK. Proxy B forwards the 200 OK to the mobile.
The call drops at the same time, every time
A call that always ends after the same time — often just under 15 or 30 minutes — is usually a session timer with no refresh. One side agreed to refresh, but its refresh never arrives, or gets a non-2xx response. The other side then ends the call with BYE, slightly before the session expires.
No refresh
Session timer: no refresh
- SIP
- ⚠Problem
Alice supports session timers and asks for an interval of 1800 s (30 minutes).
All steps as text
- Alice → Bob: INVITE. Alice supports session timers and asks for an interval of 1800 s (30 minutes).
- Bob → Alice: 200 OK. Bob accepts 1800 s and chooses himself, the UAS, as the refresher.
- Alice → Bob: ACK. Alice ACKs. Both phones start a 1800 s session timer.
- Alice → Bob: BYE. At 1768 s, no refresh has come. Alice's phone ends the call 32 s before the session expires. Problem: The call drops at 29 min 28 s, every time.
- Bob → Alice: 200 OK. Bob's phone ends the call.
Summary
- A dialog is the SIP relationship between two UAs for a whole call. Each side names it by Call-ID + local tag + remote tag; the two sides see the tags swapped.
- A 101–199 response with a To tag creates an early dialog; a 2xx creates or confirms a confirmed dialog. A non-2xx final response ends the early dialogs; a BYE ends a confirmed one.
- Each side keeps local and remote CSeq, local and remote URI, the remote target (the peer’s Contact), and the route set (Record-Route, reversed by the caller).
- Requests inside a dialog go to the remote target through the route set. Only a target refresh — re-INVITE or UPDATE — changes the remote target.
- A forked INVITE can create many early dialogs and more than one 2xx. The caller ACKs every 2xx and ends the extra dialogs with BYE.
- Two re-INVITEs that cross get 491 Request Pending; each side retries after a random wait.
- With session timers, the refresher refreshes at half the interval; without a refresh, the other side ends the call just before it expires.