Call lifecycle
Use lifecycle callbacks to keep call UI synchronized with real-time call progress. The diagrams below show the two main perspectives: placing a call and receiving one.
Outbound call (caller perspective)
When your app calls serverCall(), the callee leg progresses through status updates reported by onLegStatusUpdate.
Inbound call (callee perspective)
When your app receives a call invite, the local call transitions through invite handling into an active call.
Callback sources
onLegStatusUpdatereports callee leg progression:Ringing,Answered,Completed. Use this to update outbound call UI (e.g. show "Ringing..." then "Connected").onCallInvite/onCallInviteCancelhandle inbound invite lifecycle. See Receive an inbound call for cancel reason details.onCallHangupfires when the call ends for any reason, on both outbound and inbound calls. Always use this as the final cleanup trigger.onCallMediaDisconnect/onCallMediaReconnecting/onCallMediaReconnectionreport media-layer interruptions. See Reconnection for details.
Status updates
onLegStatusUpdate provides callee leg progression (for example: ringing -> answered -> completed). This is most relevant for outbound calls where you want to reflect the callee's state in your UI.
client.setOnLegStatusUpdate { call, legId, status ->
when(status) {
LegStatus.ringing -> println("Leg $legId for call $call is ringing")
LegStatus.answered -> println("Leg $legId for call $call was answered")
LegStatus.completed -> println("Leg $legId for call $call was completed")
}
}
Initiating hangup
Use hangup to terminate your call.
// Callback
client.hangup("callId") {
it?.let { err ->
// Handle Error hanging up call
println("Error hanging up call: $err")
}
}
// Coroutine
try {
client.hangup("callId")
} catch (e: Error) {
// Handle Error hanging up call
}
Handling hangup errors
hangup() can fail if the call has already ended, the session expired, or there is a network issue. Always handle the error to ensure your UI transitions to the ended state regardless:
client.hangup(callId) { error ->
if (error != null) {
// Hangup request failed — but the call may still end via server-side timeout.
// Clean up local UI anyway to avoid a stuck call screen.
println("Hangup error: ${error.message}")
}
// Always transition to end-of-call UI
showCallEndedUI()
}TIP
Even if hangup() fails, always clean up your local call state and UI. The server will terminate the call independently via timeout if the client-side hangup did not reach it. Waiting for onCallHangup as a secondary confirmation is good practice.
Receiving hangup events
Use onCallHangup to handle final call teardown and UI cleanup.
client.setOnCallHangupListener { call, callQuality, reason ->
println("Received $reason callQuality: $callQuality for Call $call")
}
Debugging lifecycle events
If call events are not arriving as expected, enable verbose logging during development to trace lifecycle transitions. See Logging and diagnostics for full configuration options including custom loggers and topic filtering.
NCCO hangup behavior
connectNCCO behaves like standard two-party calling — when one party hangs up, the point-to-point connection ends and both legs are terminated.conversationNCCO behavior is conversation-scoped — hanging up only removes that participant's leg from the shared room. Other participants remain connected until they leave individually.