Call State Lifecycle
A call is a state machine. Understanding every state and transition is essential for building a reliable UI and handling edge cases like reconnection, transfer, and one-way audio.State Diagram
All States
Outbound Call States (Detailed)
new → ringing
- SDK creates a PeerConnection
- ICE gathering starts (host → srflx → relay candidates)
- SDP offer created with codec preferences
- INVITE sent over WebSocket to VSP
- VSP translates to SIP INVITE → carrier
ringing
- Remote phone is ringing (SIP 180 Ringing)
- You may hear ringback tone (generated locally by the SDK or played from network)
ringing → active
- SIP 200 OK received from carrier
- SDP answer processed — codecs and ICE candidates agreed
- DTLS handshake completes — media is encrypted
- SRTP audio starts flowing in both directions
- Audio element auto-created and attached to DOM
Inbound Call States (Detailed)
ringing (incoming)
- VSP receives SIP INVITE from carrier
- VSP pushes invite message to SDK over WebSocket
- SDK creates a Call object with
state: 'ringing' telnyx.notificationfires withcallUpdate
ringing → active (answer)
- SDK sends 200 OK over WebSocket
- getUserMedia() — browser requests microphone permission
- ICE gathering starts
- SDP answer sent
- DTLS handshake
- Media flows
call.answer() triggers getUserMedia(). If the user hasn’t granted microphone permission, the browser will show a permission dialog. The call won’t be fully active until permission is granted.
ringing → destroyed (reject)
Active Call States
active → held
- SDK sends re-INVITE with
sendonlymedia direction - Remote party’s audio continues (they hear hold music if configured)
- Your audio stops sending (microphone muted at SIP level)
- Remote party receives a
callUpdatewith their call state changing
held → active
- SDK sends re-INVITE with
sendrecvmedia direction - Two-way audio resumes
Reconnecting State
- ICE connectivity checks fail (network change)
- DTLS session breaks
- WebSocket still connected but media path lost
- ICE restart — re-gathers candidates
- Attempts to re-establish DTLS
- If successful →
call.state → 'active'(call resumes) - If fails after timeout →
call.state → 'destroyed'(call drops)
Destroyed State
All calls end up here. It’s terminal — no further transitions.
Your app: Clean up UI. Upload call report. Show call summary if applicable.
State Transition Matrix
Common Pitfalls
Double answer
Not handling destroyed
Missing reconnecting
See Also
- Call Class — Methods for each state
- INotification — All notification types
- Handle Reconnection — Reconnection flow
- Handle Multiple Calls — Hold/resume patterns
- How WebRTC Signaling Works — SIP message flow