Open a call
Build oneCallHooks per call. Give it to the agent, then open the call with a greeting.
open_callholds the caller’s audio, so the greeting is not talked over.- It runs your checks and the prefetch, then greets.
- On LiveKit the session’s close ends the call. On Pipecat, call
await call.end(...)afterrunner.run().
UnmuteAgent or UnmuteFlows, each piece still works on its own. See Add one piece to your agent.
Start values
Name the values a dispatch must send, then save them before the session starts.dispatchedis the dispatch’scall_startobject. On LiveKit it isjson.loads(ctx.job.metadata or "{}").get("call_start"). On Pipecat it isrunner_args.body.get("call_start").- A missing required value, or one of the wrong type, raises
CallStartError. Nothing is saved. - For a local run, set
UNMUTE_CALL_STARTto a JSON object. The dispatch’s values win over it.
Greet the caller
AGreeting says how the call opens.
- A fixed line may name State values that prefetch or
call_startfills before the greeting. - If a named value is still empty when the call opens, the model words the greeting instead.
Greeting(interruptible=False)keeps the caller from cutting the greeting off.
Check services before the greeting
Add abefore_call hook for each service the call needs.
- The checks run in order, before the prefetch. They share
ready_timeout_s. - A check that raises or runs out of time plays the failure line and hangs up.
Tools on events
AnEventTool runs a tool without the model. This one sends the call to a CRM when it ends.
- The events are
first_user_message,call_end,tool_succeeded:<tool>andtool_failed:<tool>. - Every required argument must be locked. The model never fills one.
assign={"booking_ref": "ref"}saves a key of the result into the State.note=Trueshows the result to the model.
Handle a failure
When the model or the speech to text fails for good, the call says a short line and hangs up.handler()saysfailure_lineand hangs up. A hook that never calls it keeps the call going.- A voice failure alone only logs a warning. The call stays open.
- On Pipecat, keep the worker’s
processor_unusable_policyatCONTINUE, so the line can play.
Act when the call ends
Watchcall.ended to save what happened.
- The end runs once. It settles held saves and runs the
call_endtools first. - Then it tells the
call.endedobservers and waits for each. CallEndholdsreason,values,confirmed,transcriptandduration_s.
Why the call ended
CallEnd.reason and {{call.end_reason}} hold one word. It is the same on LiveKit and Pipecat.
When your own code ends the call, say why first. The first reason wins.
call= to max_duration, idle_nudges, end_call_tool and the other helpers. Without it, the end reads ended.
Where to go next
Call lifecycle reference
Every argument, the order of a call, and each rule in detail.
Prefetch
The work at call start, before and beside the greeting.
Hooks
Your code around prompts, model requests and tools.
The conversation key
How
agent.yaml declares the greeting and idle nudges.