agent.yaml is the declarative description of the agent: what it says, what it
can do, and who it can reach. Nothing in it belongs to one target; the runtime
half lives in targets.yaml.
An optional manifest: manifest.yaml links the company contract copied into the
package root. If that file exists, the link is required. See
Manifest for the rules and saved defaults.
A package compiles onto unmute-core for LiveKit and
Pipecat. Twilio and SLNG read the same file and run it with their own runtimes.
Each target refuses the keys it cannot run. See What each target compiles.
An agent.yaml written for unmute 0.8 or older has a version: line, and unmute
refuses it. See Migrate an older package.
YAML decoding is strict. An unknown field is an error with the file and the
line, not a shrug.
Durations use Go duration syntax, for example 90s, 15m, or 1h30m.
agent.yaml
channels: and capacity: live in targets.yaml, above targets:.
That turn.detector binding runs as written on Pipecat, which forwards a
model identity unchecked. silero is a voice activity detector, not a turn
detector, and LiveKit checks the identity and refuses it. A LiveKit target
names its own detector in targets.yaml:
targets.yaml
agent.yaml, two targets, and only the override changes. That is the
split this page and targets.yaml hold between them.
All keys
string
required
What the deployed agent is called. Accepts lowercase letters, digits, single hyphens; 3
to 64 characters. Required; omission is refused.
string
required
Agent that answers. Accepts declared agent name. Required; omission is refused.
object
Starts the call on the agent a State value names, read after the prefetch. Keys
value, the State field to read, and agents, a list of - <value>: <agent> pairs.
Omit to start on entry_agent. Twilio and SLNG refuse it.cascade | realtime | live
Which pipeline to build. Left out, it is
cascade. It decides which models
sections are legal, so it is read before the rest of the file. See
Architecture.map
required
Model palette grouped by kind. Accepts sections
think, speak, listen, turn,
and the speech to speech sections realtime and live.
Required; omission is refused.string
Listen entry to use. Accepts declared
models.listen name. Selects the sole listen
chain head; required when there are two or more, unless every cascaded agent names its
own listen:.string
Turn entry to use. Accepts declared
models.turn name. Selects the sole turn entry;
required when there are two or more.list
Where a value comes from and which step confirms it. One entry per value, each with a
name: that is a field on State in state.py, plus a source: or a confirm:. Types,
defaults and descriptions live in state.py. If omitted, no value has a source or a
confirmation.list
Facts resolved once per call, before the greeting, in the order written. Accepts one
entry per fact, each with a
name:. If omitted, no pre-fetch runs.list of strings
Environment values the generated project reads. Accepts UPPER_SNAKE names. If omitted,
there is no explicit secret inventory; inferred requirements still apply and missing
declarations warn.
map
Folders of documents a tool can search. Accepts 3 to 64 characters of
[a-z0-9_]. If
omitted, there are no document bases.list
required
Agents, each with a
name:, a prompt, models, and the tasks, flows, handoffs and
transfers it owns. Accepts one or more lower snake case names. Required; omission is
refused.list of objects
Behaviour for tools that need some:
save:, outcomes:, hidden: or start:. Every
file in tools/ is already a tool. A bare name here is refused. If omitted, no tool
has package-wide behaviour.object
Greeting, interruption, inactivity, and limits. Accepts keys below. If omitted, code
targets open with a model-written greeting and default interruption behavior. SLNG
requires an explicit greeting.
object
Tracing provider. Accepts
provider: langfuse, provider: logfire or provider: coval. If omitted, no
tracing is configured.name
What this agent is called. Required on every target.
agent.yaml
acme-support on a target called slng deploys as acme-support-slng, and on
a target called livekit_eu as acme-support-livekit-eu. The target half is
there for one collision the package half cannot solve: a package with two
targets of the same provider would otherwise deploy one name twice and overwrite
itself.
Where the deployed name lands:
name: on its own, without the target, labels the generated project: the
pyproject distribution name, the logger and the trace name.
Why unmute does not infer it
Both candidates look like names and neither is an identity.- The target is called
slng,livekitorpipecat, because that is what the docs, the examples and the console all call it. Unmute used to deploy under it, so two packages in one organisation claimed one live agent and the second deploy replaced the first, prompt, models and attached tools included. - The folder is named by whoever cloned the repository. It changes on a rename, a copy, or a CI checkout into another path, and it changes silently.
Shape
Lowercase letters, digits and single hyphens, starting with a letter, 3 to 64 characters. The name is written into a PEP 508name = in pyproject.toml, a
Pipecat Cloud agent, a LiveKit agent_name and an SLNG agent. SLNG is the
loosest of the four and pyproject the strictest, so unmute holds one shape all
four accept rather than rewriting yours per target.
Renaming an agent that is already deployed
A rename does not move a deployment. It leaves the old one running and creates a second, so after changingname::
- slng: the old agent stays in your organisation. Delete it, or leave it and point your sessions at the new id.
- pipecat:
pipecat cloud deploycreates a new agent, and the old one keeps billing. Delete it withpipecat cloud agent delete <old-name>, and re-create the secret set under the new name. - livekit: the agent itself is fine, because
lk agent deploytargets the id in the preservedlivekit.tomland re-registers the worker under the new name. The SIP dispatch rule is what breaks: it still names the old worker, so inbound calls ring and nothing answers. Delete it (lk sip dispatch delete <id>) and create it again with the new name.
livekit was
named after the target, and the new name always carries the package half, so the
first compile after this change renames every existing deployment once. Do that
rename deliberately, with the steps above, rather than discovering it on a call.
models
Six sections. The section an entry sits in decides its kind: think (LLM),
speak (TTS), listen (STT), turn (turn detection), and realtime and
live, a
live model that does the first three as one and compiles on
both code targets. The first four are maps keyed by entry name; live is a list
whose items carry name:. Entry names share one namespace across sections and
are yours to choose.
A
turn entry’s provider is local (the on-device pair) or, on Pipecat,
listen, which hands the decision to the listening model’s own turn detection.
listen takes no model of its own and needs a Deepgram Flux or Cartesia Turns
listening model. Under listen the pace ceiling becomes the transcriber’s own
end-of-turn timeout, and semantic_endpointing, endpointing_delay and
interruption.minimum_words are refused because nothing reads them; see
Turn detection.
| fallback | think, listen |
| name, provider, model, voice, backend, description | live, and nothing else: every other field is refused on a live entry by name, with its line. backend names a models.think entry with provider: openai that runs the model’s tools and reasoning. See Live model |
| name, provider, model, voice, turn_detection, description | realtime, and nothing else. turn_detection is server_vad, semantic or local. voice and an agent speak: binding are mutually exclusive, and neither is refused. See Realtime |
| prompt_suffix | think: literal text appended to every prompt this binding sends, up to 512 characters, no {{variables}}. A per-target override cannot name a different value. See Context Router |
| agent_id, upstream | think, and only on a binding routed through the SLNG Context Router; refused on any other binding. See Context Router |
pace and endpointing_delay are the two turn-timing settings and they do
different jobs: the pace sets the ceiling on a turn, the duration sets only the
floor. Turn taking has every legal value and what
each becomes on each target.
string
A provider supported for this role and target, listed on the model pages. Required for
an API binding; there is no inferred API provider.
local selects local placement.string
Provider model id, passed through as written. Required where the selected integration
requires a model; otherwise its default applies. LiveKit turn bindings accept only
turn-detector-mini or turn-detector.string
Voice id on a
speak entry. No voice is chosen by Unmute when omitted; the selected
integration may require one or use its own default.number
Speaking speed on a
speak entry. Provider-defined values and limits; omitted leaves
the provider default.string
BCP-47 language tag, such as
en or en-US, on listen or speak. Omit to leave
language selection to the integration.number
Sampling temperature on a
think entry. Provider-defined values and limits; omitted
leaves the provider default.number
Nucleus sampling value on a
think entry. Provider-defined values and limits; omitted
leaves the provider default.integer
Sampling count on a
think entry. Provider-defined values and limits; omitted leaves
the provider default.string
An UPPER_SNAKE environment variable name holding a custom endpoint URL. Omit to use the
integration’s endpoint. Required for Pipecat’s unlisted-provider path.
string
Accepts
api or local. If omitted, provider: local selects local placement; another
named model selects API placement. Target-specific turn detection may decide placement
itself.object
Provider parameter names and values. Omit to add no extra parameters. Provider limits
apply; Unmute does not define a universal accepted set. The Responses directive below is
checked separately.
list of strings
Names from the same
think or listen section, in retry order. Omit for no fallback
chain. Cycles and other roles are refused; Pipecat refuses generated fallback.string
An author note. Omit for no note.
string
On
turn, accepts required, preferred, or off. Omit to keep the target’s semantic
detector. off removes it; see turn
detection.string
On
turn, accepts snappy, balanced, or patient. Omitted means balanced. Sets
the ceiling and, unless endpointing_delay is present, the floor. Cannot be authored in
a per-target override.string
On
turn, a positive Go duration such as 300ms. Sets only the silence floor; LiveKit
requires at least 250ms. Omit to use the pace’s floor. See turn
taking.string
Required on a Context Router think binding: a stable id of at most 128 printable ASCII
characters, with no whitespace or colon. No id is generated. See Context
Router for scope rules.
object
Required on a Context Router think binding; no upstream is inferred. Names the provider
and its credentials. See upstream
fields. Refused on other bindings.
string
Literal text on a
think entry, up to 512 characters, with no {{variables}}. Appended
to every prompt using the profile. Omit to append nothing. A target override cannot
declare a different value.endpoint_env, through an
OpenAI-compatible integration. LiveKit accepts an unlisted think provider
through LiveKit Inference; its listen and speak provider lists are closed.
Never invent a provider name for either route.
model and voice are passthrough. Most params are too: a name the target’s
settings object has no field for rides that target’s overflow field and reaches
the provider. The narrow exception is api: responses on a LiveKit OpenAI
reasoning binding. Unmute checks that directive, selects the Responses client,
and turns reasoning_effort into the API’s nested reasoning setting.
Reasoning model shows the target-local form and explains why.
Do not guess model ids, voice ids, or params. Use values the user supplied or
values verified in the provider’s own documentation.
variables
State class in state.py, next to agent.yaml. List a value here only
when it needs a source: or a confirm:. An entry with neither is refused,
and so is the old map form. A shapes: block is refused too: declare each
shape as a Pydantic model in state.py.
string
required
The field on
State this entry configures. An entry naming no field is refused.string
Accepts
call_start, conversation (SLNG only), or a system call
fact. If omitted, the variable has no automatic call-fact
source; a task, pre-fetch, or session payload can still fill it. Route support is
checked for system sources. conversation requires a Field(description=...) and an
= None default in state.py. Inbound code-target calls require a default for
call_start.string
The name of the task that must hear the caller agree to a pre-fetched value. Until
confirmed, only that task can read it in a prompt and tools cannot silently use it. If
omitted, the value is usable as soon as it arrives.
{{state.name}}, or
{{state.name.field}} for one field of a model. Full detail on
variables, and on what state.py can hold:
state.py.
prefetch
writes: declared on the entry. Entries resolve top to bottom, and
one that cannot resolve is skipped rather than failing the call.
A prompt and a greeting read State as {{state.x}} and {{state.x.y}}, and read
what the call carries as {{call.x}}. A line code says next to a tool reads more: a
confirm: line reads the tool’s {{input.x}}, and an outcome’s say: reads
{{result.x}}. No helper save: is needed to say a value.
A clock: entry also carries its own timezone:, an IANA zone name. It is
required there, never defaulted, because a container’s own clock is UTC, so a
business elsewhere needs it to date a call correctly. It sits on the entry
rather than on the package, because two entries may honestly want two zones.
An entry can also carry refuse_call:, a line said when the lookup finds nothing.
The call is then refused with that line. Twilio and SLNG refuse the key.
A call: entry runs one tool, passes its inputs with with:, and needs writes: true or writes: false. There is no default,
because a pre-fetch runs unasked on every call. writes: true compiles: it is
named in compile-report.json and the runbook, rather than printing a
warning.
Full field reference, the ordering rule, every result field a clock gives you,
and what an empty value does to a prompt:
variables.
secrets
{{template}}. That list is a worked example: two model keys, the four
names a SIP connection maps, and the two desks the agent can transfer to.
Declare every environment name the generated project reads. That means names
written in tool and connection fields, transfer to: values, literal os.environ
reads in local handlers, provider API keys inferred from the model catalogue,
and the names your tracing provider needs. Names the driver or platform
supplies, such as REDIS_URL or DAILY_API_KEY, stay out. See
secrets.
knowledge
Each base has a name of 3 to 64 characters using [a-z0-9_]. The name is
the map key and becomes the search collection and build folder name.
string
required
Path to a folder inside the package containing
.txt, .md, or .pdf documents. No
folder is inferred.string
An embedding service. Omitted means
openai.
Keyword mode makes no embedding call.string
Accepts
meaning, keyword, or hybrid. Omitted means hybrid.integer
Passage size in tokens, from 1 to 2048. Omitted means
90.integer
Tokens shared by neighboring passages, from 0 through
chunk_size. Omitted means 20.integer
Maximum passages returned by a lookup, from 1 to 20. Omitted means
3.number
Minimum accepted result score, from 0 to 1. Omit for no score filtering. Scores are
similarities, not probabilities.
mode: keyword is the one with a structural
consequence: it uses BM25, needs no embedding service, no credential and no
network call, and the emitted image installs no embeddings package. min_score
needs care, and needs mode: meaning to do anything useful. These are
similarity scores, not probabilities, and in practice they land well below 1,
so a value near 1 returns nothing. On hybrid the unscored keyword results
pass through any cutoff. See Knowledge bases for how
to set it. top_k times chunk_size is roughly what reaches the model on
every lookup, and the compiler warns above about 1500 tokens.
A tool reaches a base by name, with a
knowledge: block, and an agent reaches it by being
given that tool. The base selects a search mode and may set a minimum score.
Full behaviour, every message, and what a lookup gives the model:
Knowledge bases.
agents
string
required
A lower snake case name, unique across the package.
string
required
Path to a Markdown prompt inside the package. No prompt is inferred.
string
required
Name of an entry in
models.think. No profile is inferred. Required under
architecture: cascade, and refused under the other two, where one model does
this job.string
Name of an entry in
models.realtime, in place of think and speak. Legal
only under architecture: realtime.string
Name of an entry in
models.live, in place of think and speak. Legal only
under architecture: live.string
required
Name of an entry in
models.speak. No profile is inferred. On livekit and pipecat
each agent speaks with its own voice after a handoff.string
Name of an entry in
models.listen, for an agent that hears another language. Omit to
use the package’s listen model. Cascaded agents only: a realtime or live agent hears the
caller itself, so listen: there is refused. This entry cannot have a fallback: list.
The slng and twilio targets refuse one that differs from the package’s; livekit and
pipecat hear each agent with its own.list of strings
Names of loaded tool files this agent may call. Omit for no ordinary tools.
list of definitions or names
Nested task definitions or bare names of tasks defined by another agent. Omit for no
tasks. See task fields.
list of definitions or names
Ordered task sequences. Each item is a block this agent defines or the bare name of a
flow another agent defines. Omit for no flows. See
flows.list of definitions or names
Agent to agent, and never returns. Each item is a block this agent defines or the bare
name of a handoff another agent defines. Omit for no agent handoffs. See
handoffs.list of definitions or names
Agent to a person. Each item is a block this agent defines or the bare name of a
transfer another agent defines. Omit for no human transfers. See
transfers.tasks
A task is nested inside the agent that defines it, not written in a top-level
catalog. Each item of an agent’s tasks: list is either a full definition or
a bare string naming a task another agent already defines:
string
required
A lower snake case name, unique across all agents in the package. To reuse an existing
task, write its bare name instead of defining it again.
string
required
Path to the task’s Markdown prompt inside the package. No prompt is inferred.
string
The situation the model reads to decide whether to start the task. If omitted, the task
is a definition only and must be used in a flow; it cannot be attached elsewhere
by bare name.
list of strings or objects
Tools loaded by the package. Each item is a bare tool name, or a block that starts
- name: <tool> and adds confirm: and outcomes: for this step only. Omit for no
ordinary tools in this task; it does not inherit its owner’s tools.list of strings
Lines said to read the call back before the tool runs. The tool runs on the caller’s
yes. Lines read
{{input.x}}. Needs a then: done outcome on the same tool.list of objects
What the step does with each result of this tool, top to bottom, first match wins. The
rules are in Say what success looks like.
list of one-key pairs
A typed match on the result, such as
- status: booked or - status: [booked, moved].null
Matches every result no
if: above matched. Last, and at most one.list of one-key pairs
Pairs of
variable: result.field, saved only when this outcome matches.list of strings
Lines said for this result; several are alternatives. Lines read
{{result.x}}.string
Accepts
done or failed. Omitted means the step goes on.list of strings
Bare names of handoffs an agent defines. Omit for no handoffs from this task.
list of strings
State fields the model fills from the conversation when it starts the task. Omit to
fill none.list of one-key pairs
Pairs of
variable: result.field, including dotted result paths. Use variable+ to
append one list item, and result.field? for a value the step may leave out. Omit to
save no values; the task can still finish. Types and descriptions come from the
destination fields on State.object
What the step does as it opens. Omitted means the model writes the opening turn. Keys
below.
list of strings
Fixed lines with no
{{placeholders}}, said as the step opens. Several are
alternatives: the caller hears one each time, never the one used last.boolean
true waits for the caller after say:, with no model request.string
A tool of this step, run in code before the model speaks.
list of one-key pairs
The tool’s inputs, as
- day: state.requested_day. A ? after the value lets the call
run without it; a value without one is required. See
Start a step.string
A
models.think entry name. LiveKit only. If omitted, use the entry agent’s think
profile, even when another agent defines the task.object
The history fields. Omit for
history: messages. A
returning task restores its owner’s earlier context and adds only completion or unserved
status.result, expect, and requires are retired task fields. A package that
writes one gets a located migration error. Declare variables, save them with
save:, and reference only the values a receiving prompt needs.
Two agents defining a task under the same name is refused:
when makes a task callable; save says which values it saves.
A task may omit save: and still finish. Every generated finish also takes
optional unserved_request; it returns only an unserved status to the owner.
A webhook tool keeps its input: and output: JSON Schemas. A local tool has
neither: its handler’s types are the contract.
context
Used by tasks, flows and handoffs.
string
Accepts
full, messages, last_n, summary, or reset. Omitted means messages:
keep speech and remove tool calls with their replies. summary is LiveKit only; SLNG
refuses authored context settings.integer
Required and positive with
history: last_n. There is no default count. Refused with
other history modes.string
Required with
history: summary: a models.think entry name. No model is inferred.
Refused with other history modes.boolean
Accepts
true or false. Omitted behaves as true in modes that keep tool records. It
cannot add tool records to messages or reset. Explicit false is LiveKit only.include_tool_calls: true does not add tool records to messages or reset.
To keep old tool results, use full or last_n. Pipecat supports those two
modes plus messages and reset; it refuses summary and explicit
include_tool_calls: false. SLNG takes messages, full, last_n and
reset on a task, refuses summary and include_tool_calls: false, and has
no handoffs.
For example, these are two separate context blocks:
flows
An agent runs a flow by defining it in its own flows: list. The flow carries
its own when:, the situation the model reads to decide whether to run it. Another
agent runs the same flow by writing its bare name:
string
required
A lower snake case name, unique across the package.
list of strings or objects
required
One or more task names, in execution order. An object requires
task and may set
skip_when_confirmed to a variable that task confirms. Omit that condition to run the
step every time. An empty or absent steps list is refused.string
The situation the model reads to decide whether to run the flow. Omission is accepted
but supplies no trigger guidance, so write one.
string or list of strings
One fixed spoken line, with no
{{placeholders}}, when the flow starts. Omit for no
announcement.
Write several as a list and the caller hears one of them each time the line fires.
Never the one that site used last. A caller who comes back through the same flow
does not hear the identical sentence twice.It fires on entry, before any step runs, so it is spoken whether or not a
skip_when_confirmed: step is skipped. A line that is only true when some step
runs belongs on that step’s own start: say:, not here.list of strings
State fields the model fills from the conversation when it picks the flow. The steps
read them. Omit to fill none.string
default:"shared"
Accepts
shared or isolated. Omit it for shared. Each member still applies its own
context.history; an isolated flow cannot be widened by a member’s full.string
default:"return"
Accepts
return, transfer, or end. Omit it for return.string
Required with
then: transfer: an existing agent name. Refused for return or end;
there is no inferred destination.list of strings
Steps that run without holding the caller. Each name must be one of
steps. Omit to
run every step in turn.string
Only
results is accepted. Omission also means results.context.history. An isolated flow cannot be widened by
a member’s full.
handoffs and transfers
Everything the model can hand the caller to and not get back. Each one is a list
in the agent that defines it, so there is no kind: field.
handoffs
The conversation becomes another agent, and never comes back.
string
required
A lower snake case name, unique across the package.
string
required
An existing agent name. The conversation moves to that agent and does not return. No
destination is inferred.
string
The situation the model reads to decide whether to hand over. Omission supplies no
trigger guidance, so write one.
list of strings
Any number of fixed lines. One is picked each time the handoff runs, never the one
said last, and the next agent’s voice says it. A line takes no
{{ }} placeholder and
a blank line is refused. Omit for a silent handoff.list of one-key pairs
Pairs of
variable: result.field saved as the handoff happens. Omit to save nothing.object
A tool of the receiving agent, run in code as it takes the call. Keys
call and with.string
required
Name of the receiving agent’s tool. Required inside
start.list of one-key pairs
The tool’s inputs, as
- day: state.requested_day. A ? after the value lets the call
run without it; a value without one is required.object
The history fields. Omitted means
messages. Saved
values are visible only where the receiving prompt names them.say is exact spoken text, not a model instruction: write the short
sentence the caller should hear. Omit it for a silent handoff.
transfers
Puts the caller through to a person. A transfer is cold by default. Writing
brief: makes it warm: the agent briefs the person before connecting the caller.
string
required
A lower snake case name, unique across the package.
string
required
The UPPER_SNAKE name of an environment variable holding an E.164 number or a
sip: URI,
read at call time. Declare it in secrets:. A number written here is refused, because
agent.yaml is the portable half of a package. The model never sees a number.string
The situation the model reads to decide whether to transfer. Omission supplies no
trigger guidance, so write one.
string
A positive Go duration, such as
25s. Omitted means 25 seconds on Pipecat; LiveKit
leaves it unset for the platform default.Accepts
return_to_caller or hangup. Omitted means return_to_caller. Pipecat
cloud-websocket requires explicit hangup, because the original media stream cannot be
reconnected.string
Instructions for briefing the person before connecting the caller. Makes the transfer
warm. Supported only on LiveKit SIP.
sip; Unmute does not support warm
transfer on any Pipecat target.
A warm transfer on a route with no leg to move is refused by name:
tools
tools/ is a tool, read in name order. The top-level tools:
list holds only the tools that need behaviour around them on every agent. A
bare name there is refused. Availability is decided by the tools list on each
agent and task, which hold names only, except that a task’s list may also carry
a block for that step. Define each tool once. Put its
full contract in tools/<name>.yaml; never inline description, input,
output, with, or an execution block under a tools: list in agent.yaml.
object
A block starting
- name: <tool> with at least one of the keys below.list of one-key pairs
Pairs of
variable: result.field, saved on every result of the tool, whatever the
outcome. Package scope only.list of objects
What is said for each result, top to bottom, first match wins. Each item takes
if: or
else: and say:, with no then: here, because only a step ends. Lines read
{{result.x}}. See Say what success looks like.Result fields the model never reads. Use it for a field code speaks, such as a
read-back, so the model does not say it in its own words. Each name must be a field the
tool returns.
object
What is said as the tool starts, so a slow call is not silence. Keys
say and wait.list of strings
Fixed sentences with no
{{variables}}, said when the tool is called, not when it
succeeds. Several are alternatives: the caller hears one each time, never the one used
last. The slng target takes one line only, because an attachment carries a single
pre-action message; a list is refused there. Legal on local, webhook, knowledge, and
hosted slng tools.boolean
true runs the tool only after the say: line has played, for a goodbye heard in full
before a tool that ends the call runs. Needs a say: line.Tool files
One file per tool, intools/. The top level is the contract with the model;
exactly one execution block says how it runs. A local tool names a typed Python
function with local: handler: tools/<name>.py:<function>. The function’s
arguments are the model’s input and its Pydantic return model is the output, so a
local tool file has no input: or output:. A webhook tool writes both as JSON Schema.
string
Required for local, webhook, and knowledge tools: explains when the model should call
the tool. Builtins use their registry description if omitted; hosted
slng tools
inherit their published description. Refused on MCP sources.object
Required for authored webhook contracts: a JSON Schema with
type: object
describing model arguments. A local tool leaves it out, because its handler signature is the
input. Refused on builtin, MCP, knowledge, and hosted slng tools, which own
their schemas.object
An author-side JSON Schema with
type: object. Omit for no declared result schema. Used
by save: rows and outcome matches; it is not a general runtime result validator or a
model prompt. A local tool leaves it out, because its return model is the
output. Refused on builtin, MCP, knowledge, and hosted slng tools.list of one-key pairs
Hidden argument/value pairs. A value is
state.variable, a scalar, or a string with
{{state.variable}} placeholders. Omit to pass nothing. Legal on local, webhook, and
hosted slng tools; builtin send_sms requires its literal from_number setting.string
Accepts
provider_default, continue, or cancel. Omitted means provider_default.
Refused on MCP sources. Target support is listed above.string
Accepts
returns_data or ends_conversation. Omitted means returns_data, except
builtins whose effect comes from the registry. Refused on MCP and knowledge tools.string
file.py:function, run before the tool. It returns an outcome to refuse the call, or
None to go on. Legal on a local tool only. Twilio and SLNG refuse it.
A
webhook tool takes no precheck:. A knowledge file takes no input, output, with or effect: the tool owns
both sides of its contract, taking one string and returning passages.
An mcp file is the block and nothing else. The six contract fields above are
all illegal on one, because the server describes its own tools.
string
Accepts
sse or streamable_http. Inside mcp, omission uses the runtime’s
URL-based transport choice; a URL ending in /mcp selects streamable HTTP.current_datetime, end_call, send_sms, user_phone_number.
end_call compiles on every target. The other three are capabilities SLNG
curates, so they compile on the slng target only.
The file has to be named after the id it selects, because slng attaches a
builtin by the tool file’s own name.
On a builtin, with: pins the capability’s own settings rather than an
argument, because a curated capability publishes no argument schema. The model
never sees one either way.
send_sms takes a single from_number: the sender, as a literal number in
international format starting with a plus sign. The model supplies the
recipient and the body itself, and SLNG reads the Twilio credentials from your
vault under TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN, which unmute deploy
checks.
current_datetime takes a single timezone, an IANA zone name such as
America/Los_Angeles. Write it whenever the agent is not in UTC. The curated
tool defaults to UTC and belongs to no organisation, so it cannot be changed on
the platform. An agent without it is told the wrong date for part of every day.
A zone survives daylight saving; an offset written into the prompt does not.
user_phone_number takes no settings.
webhook.auth:
string
required
Accepts
bearer or api_key. Required when auth is present; no scheme is inferred.string
required
An UPPER_SNAKE environment variable name holding the token, never the token itself.
There is no default.
string
An HTTP header name, legal only with
type: api_key. Omitted means X-API-Key. Bearer
authentication uses Authorization: Bearer.conversation
object
Opening behavior using
speaks_first and optional say. If omitted, LiveKit and
Pipecat generate an opening line. SLNG requires an explicit greeting with say.string
Required when
greeting is present. Accepts agent or user; there is no default
inside an authored block. SLNG requires agent.string
The exact opening line, with eligible variable placeholders. Requires
speaks_first: agent. If omitted on a code target, the model writes the greeting; SLNG requires say.object
Barge-in settings. If omitted, interruptions remain enabled. Pipecat phone routes also
protect the greeting by default.
boolean
Required when
interruption is present. Accepts true or false. There is no default
inside an authored block.list of strings
Accepts
greeting, tool_calls, or both on Pipecat. Omit to protect the greeting on a
Pipecat phone route and nothing on a browser route. Set [] to protect nothing.
Non-empty protection is refused with enabled: false.integer
A positive count sets how many words count as an interruption on code targets. Omitted
or zero leaves the runtime’s default word threshold.
list of strings
Phrases that do not interrupt on code targets. Omit for no authored ignored phrases.
object
Optional
nudge_after and end_after timers. Omit for no authored inactivity timers.
SLNG refuses this block.string
A positive Go duration, such as
15s, before an idle nudge. Omit for no authored nudge
timer.string
A positive Go duration, such as
45s, before ending an idle call. Omit for no authored
idle end timer.string
A positive Go duration, such as
15m, limiting a call on code targets. Omit for no
package-defined limit. SLNG refuses this field.string
Accepts
none or subtle. Omitted means no thinking audio. subtle is LiveKit only.tracing
Tracing fields
string
required
Accepts
langfuse, logfire or coval. Required inside tracing; no provider is inferred. Omit
the whole tracing block to disable tracing. Supported on LiveKit and Pipecat; refused
on SLNG.provider takes one of three values: langfuse, logfire or coval. Tracing
works on both code targets.
LiveKit uses the room name as the session ID. Pipecat uses the runner session ID
as both its conversation ID and the session ID. Langfuse reads it as
langfuse.session.id, and Logfire as session.id.
Pipecat tracing owns the process OpenTelemetry provider and startup fails if another SDK provider is installed first.
With coval, each trace is attached to the Coval simulation that placed the
call, and the agent finds that simulation ID on the call itself. See
tracing for how the ID reaches the agent on each target.
Traces can contain caller speech, model input and output, and tool arguments and results.
Use only fake identities and fake customer data for release tests. Use a separate
project on your tracing provider for those tests, and do not send real customer
data until its access and retention rules are approved.
Reachability
Models are a palette, so an unused model entry is legal. Other declarations must be reachable fromentry_agent. An unused handoff, transfer, flow,
or non-entry agent is a build error, and so is a tool no agent’s
tools: list names. Attach it to the reachable graph or remove it.
A task is reachable a different way, because it is defined inside the agent
that lists it rather than in a top-level catalogue. A task with no when: and no
flow naming it in steps: is a build error too. Give it a when: so an
agent can decide to run it, or list it as a step of a flow that is
reached.
What each target compiles
LiveKit and Pipecat compile a package onto unmute-core.unmute compile writes
agent.json, the package’s data in the library’s names. spec.py builds the library’s
objects from it when the agent starts, so a broken package fails then, never during a
call. agent.py (LiveKit) or bot.py (Pipecat) builds the pipeline around them, and
speech.py holds any speech model that is not SLNG’s.
The twilio and slng targets run the same file with their own runtimes and without
unmute-core. They refuse these keys at compile time. The error says to remove them or
compile to livekit or pipecat:
- a tool entry’s
save,outcomes,hiddenandstart: wait, and a tool file’sprecheck; - a step’s outcomes without
then:, a stepstart:withcall:, and a step outcome withconfirm:; entry_by;- prefetch
refuse_call; - a handoff’s
saveorstart.
then: done and say: is refused on Twilio only. SLNG speaks the line itself.
Each target refuses more of its own: see Twilio
and SLNG.
LiveKit and Pipecat do not compile these yet:
What the models sections compile on LiveKit and Pipecat:
tracing: compiles langfuse there so far. transfers: transfer the caller on a phone
line, and a browser session needs no phone route variable to start. An agent’s language is
its voice’s language:, and its timezone is the clock prefetch’s timezone:. Code says
dates in them.
A package with a scenarios/ folder runs whole calls on the real model with
make e2e PKG=salon-concierge FW=livekit. See
examples/salon-concierge
for every key on this page in one agent.
Where to go next
targets.yaml
The runtime half.
Models
Which
provider: values each target accepts.