Call States and Lifecycle

View as Markdown

call.state exposes the current CallState. Use it to decide which commands are valid and when serialization (to_json) is allowed.

States

StateMeaning
NEWCall object exists; voice WebSocket not open. Only state where to_json() is valid.
OPENINGMedia connection is opening. On failure, state reverts to NEW (retry allowed).
RINGINGOutbound ring in progress (dial).
LIVEMedia up; stream and send audio. Internally tracks two-party and conference sub-states.
TERMINATEDCall over. Further send_audio raises CallClosedError. disconnect() / close() are idempotent.

Inbound lifecycle

  1. @sm.on_incoming_call receives IncomingCallNotification (addressing only, no media credentials).
  2. open_sessionprocess_call(noti)Call in NEW.
  3. Choose a path:
    • Agent answers: answer() → media opens → stream caller / send_audio.
    • Ambient / pass-through: connect() after answering to bring in the callee → then spy / whisper / barge.
  4. Hangup (remote, close, or disconnect) → TERMINATED. on_hangup fires.

Outbound lifecycle

Two entry points:

  • A. SDK-placed call: session.make_call(Address.telco(...))NEW. Then dial(ring_time_seconds=...) → rings. Falsy CommandResult with CALL_UNANSWERED / TIMEOUT if no answer.
  • B. Subscriber-originated call: @sm.on_outgoing_call fires when the subscriber’s own line dials out (requires TriggerConditionsBuilder.outbound_call). process_call(noti)NEW.

On success, stream call.callee.audio_stream() (the dialed party) and send_audio. Optionally connect() for conference-style flows, then ambient modes.

Handoff constraint

to_json() / from_json() only while CallState.NEW. After media opens, serialization raises CallStateError. See Sessions and Handoff.

Ending

MethodEffect
close()Release the agent. After a successful connect(), caller and callee can stay connected. After answer() alone, the call ends for both.
disconnect()End the call for all parties.

Next Step