Skip to main content
The library runs what happens around a call. It saves the values the call starts with, greets the caller, runs tools on events, handles a failure and closes the call. It works the same on LiveKit and Pipecat. In the salon tutorial, Quiet callers and long calls builds this step by step.

Open a call

Build one CallHooks per call. Give it to the agent, then open the call with a greeting.
  1. open_call holds the caller’s audio, so the greeting is not talked over.
  2. It runs your checks and the prefetch, then greets.
  3. On LiveKit the session’s close ends the call. On Pipecat, call await call.end(...) after runner.run().
On a plain agent, with no 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.
  1. dispatched is the dispatch’s call_start object. On LiveKit it is json.loads(ctx.job.metadata or "{}").get("call_start"). On Pipecat it is runner_args.body.get("call_start").
  2. A missing required value, or one of the wrong type, raises CallStartError. Nothing is saved.
  3. For a local run, set UNMUTE_CALL_START to a JSON object. The dispatch’s values win over it.

Greet the caller

A Greeting says how the call opens.
  1. A fixed line may name State values that prefetch or call_start fills before the greeting.
  2. If a named value is still empty when the call opens, the model words the greeting instead.
  3. Greeting(interruptible=False) keeps the caller from cutting the greeting off.

Check services before the greeting

Add a before_call hook for each service the call needs.
  1. The checks run in order, before the prefetch. They share ready_timeout_s.
  2. A check that raises or runs out of time plays the failure line and hangs up.

Tools on events

An EventTool runs a tool without the model. This one sends the call to a CRM when it ends.
  1. The events are first_user_message, call_end, tool_succeeded:<tool> and tool_failed:<tool>.
  2. Every required argument must be locked. The model never fills one.
  3. assign={"booking_ref": "ref"} saves a key of the result into the State. note=True shows 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.
  1. handler() says failure_line and hangs up. A hook that never calls it keeps the call going.
  2. A voice failure alone only logs a warning. The call stays open.
  3. On Pipecat, keep the worker’s processor_unusable_policy at CONTINUE, so the line can play.

Act when the call ends

Watch call.ended to save what happened.
  1. The end runs once. It settles held saves and runs the call_end tools first.
  2. Then it tells the call.ended observers and waits for each.
  3. CallEnd holds reason, values, confirmed, transcript and duration_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.
Pass 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.