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
| State | Direction | Description | What your app should do |
|---|---|---|---|
new | Outbound | Call object created, ICE gathering | Show “Connecting…” |
ringing | Outbound | Remote party’s phone is ringing | Show ringing UI, play ringback tone |
ringing | Inbound | Incoming call waiting | Show incoming call UI, ring tone |
active | Both | Call connected, media flowing | Show in-call UI, start timer |
held | Both | Call on hold (sendonly) | Show held state, dim audio |
reconnecting | Both | Media path lost, attempting recovery | Show reconnecting banner |
destroyed | Both | Call ended | Show call ended, clean up UI |
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.| Cause | Direction | Why |
|---|---|---|
normal | Both | Normal hangup — either party ended the call |
originatorCancel | Outbound | Caller hung up while ringing |
timeOut | Outbound | No answer within timeout period |
rejected | Inbound | Callee rejected the call |
error | Both | Network error, ICE failure, or server error |
replaced | Both | Call was replaced (attended transfer) |
State Transition Matrix
| From | To | Trigger | Direction |
|---|---|---|---|
new | ringing | INVITE sent/received | Outbound |
ringing | active | answer() / 200 OK | Both |
ringing | destroyed | hangup() / reject / timeout | Both |
active | held | hold() | Both |
held | active | unhold() | Both |
active | destroyed | hangup() / BYE | Both |
held | destroyed | hangup() | Both |
active | reconnecting | ICE/DTLS failure | Both |
reconnecting | active | Media restored | Both |
reconnecting | destroyed | Timeout | Both |
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