The tool model
A tool (ToolCall) exposes a single function to the LLM. When the model calls it, the agent runs the tool’s ordered list of actions, then renders an output the model can read back.
Actions execute in order. Each action whose
condition passes runs, and later actions, the output template and skip_reply can read what it returned through steps or data.
Reading earlier results: steps and data
Every action of the tool has an entry in steps, from before the first one runs:
An action’s
<id> is its id field when set, and otherwise its position counted from 1, as the editor numbers actions: steps.1, steps.2, and steps.on_error_1, steps.on_error_2 in actions_on_error. A position changes when you reorder actions; an id does not. An id starts with a letter, uses only letters, digits and _, does not start with on_error_, is not the name of a dict method (items, keys, values, get, update, pop, popitem, setdefault, copy, clear, fromkeys), and is unique within the tool.
data is the last value any action returned. It is not always the data you mean: on text and email, play_message returns {"message": ...} and handoff returns {"handoff": ...}, so either one replaces a workflow’s result for every action after it. On voice, play_message returns nothing. Read a specific result with steps instead:
data.allow_transfer in the last condition, the transfer would not happen on text channels: by then data is the message. Read an earlier action’s result through steps, not data, once anything else has run after it.
Workflows also receive steps in their input, next to arguments and data.
The tool’s own variables (
arguments, data, steps, and error and refused in skip_reply) win over a conversation custom_metadata key of the same name. The stored value is still available as custom_metadata.<key>, and the conversation logs a warning the first time a stored key is hidden this way. Workflows cannot save custom_metadata keys named arguments, data or steps. Workflows that no tool runs (pre- and post-conversation workflows) are unchanged: they get an empty arguments and data under the conversation’s variables, and no steps.Skipping the model’s reply
By default the model replies after every tool call, reading the tool’s result.skip_reply lets a tool end the turn on what its own actions said, for example a confirmation read out word for word. It is an expression, checked after the tool finishes, that can read:
"true" always skips. "steps.1.output.booked" skips once a booking succeeded and lets the model explain a failure. "error" skips only when the tool failed, after a play_message in actions_on_error apologised.
What the customer gets instead:
- Voice and web chat: what the tool’s
play_messageactions said, each as its own message, in the order the actions ran. Nothing more is added. - Text API and email, which send one message per turn: that step’s
play_messagemessages become the reply, in the order the actions ran, after anything the model wrote alongside its tool calls. - A
play_messagewithgenerate: truestill produces its message on every channel, as one completion from its instruction, without tools, made where its action ran and told what was said before it. On web chat a word-for-word message is sent as soon as its tool has run, unless a generated one comes before it: then it waits its turn, so the visitor reads them in action order. - If no action said anything, the customer gets nothing this turn.
skip_reply and count as needing a reply (a web search’s results still need answering), except set_thinking_hint, which only changes the “thinking…” text and has no say. A handoff still ends the turn as it always does. On web chat, a message the customer sends while the tools run supersedes the turn as it would a reply (nothing more is generated, and both messages are answered together) unless a tool has already said something or the visitor has seen part of the turn. A tool’s messages, word for word or generated, are always delivered, like canned ones: the turn then finishes and the newer message is answered next. Realtime voices that write their own tool replies (Gemini Live, Nova Sonic) cannot have the reply skipped. An expression that fails to evaluate is logged and counts as false.
In the agent editor, Tools & Actions lists every tool and opens each one in an editor laid out like this model: the description, the inputs (as a table, or as a JSON Schema for nested objects and arrays), the actions in order, the actions to run if one fails, the reply to the model, whether to skip the model’s reply, and the identity settings. Drag actions to reorder them, give any action a condition from its menu, and name it with Step ID when you open it. The variable picker in a condition or template lists the steps that have run by that point.
Every action carries an optional
condition expression and an optional id. When condition is set, the action runs only if the expression evaluates truthy against the tool call’s arguments, data and steps. Leave it empty to always run the action. id names the action’s entry in steps.Timing of a tool call
Every tool call’s output in the transcript carries atiming record, and the agent logs it as a tool_call_timing line when the call returns. A conversation’s tool calls show it under Timing when you open them. Times are epoch seconds; durations are seconds.
On text, email and web chat, the turn’s reply also records
received_at (the customer’s message), started_at and completed_at (the reply handed to the channel). Where a tool’s message goes out with the turn (text, email, web chat that is not streamed) it has no said_at: completed_at is when it went.
So, for a turn whose reply was skipped, end-to-end latency is the first said_at (or completed_at) minus the customer’s end of speech: stopped_speaking_at on the caller’s message on voice, received_at elsewhere.
Action catalog
Actions form a discriminated union keyed ontype. The full set:
Transfers, conferencing, and DTMF have their own page. See /agents/transfers for the call-routing actions (
cold_transfer, warm_transfer, conference, dtmf).play_message
workflow
Runs a workflow either by reference (workflow_id + workflow_version) or as an inline definition. The two sources are mutually exclusive.
The workflow runs synchronously. The tool-call arguments and conversation data are passed as input, and the workflow’s
output (or its context if there is no output) becomes the action result.
A reference stays on the version it names; publishing a newer version of the workflow does not change the agent. In the agent editor, add a Run a workflow action to a tool under Tools & Actions. See In-call workflow tools.
knowledge_base
Runs a retrieval query against a knowledge base dataset and returns the top matches ascontent and score pairs.
wait
play_audio / stop_audio
stop_audio takes only condition and stops the currently playing file.
update_endpointing
Adjusts turn-detection timing mid-call. Both delays are optional; leave one unset to keep its current value.hangup / leave_room
Both take onlycondition. hangup ends the call; leave_room removes the agent from the room.
Which actions run on which channel
The action catalog is built for telephony. On the async text and email engine there is no audio playout or call control, so only three action types execute:On async channels,
play_message has no audio playout. The rendered message text is returned as the action output so the tool’s output template can reference it. Any voice-only action is skipped and logged, not failed.Related
Tools
How the LLM decides to call a tool and what arguments it passes.
Transfers and call routing
Cold transfer, warm transfer, conference, and DTMF in depth.
Workflows
Build the workflows a
workflow action runs.Knowledge bases with agents
Connect datasets a
knowledge_base action can query.
