Skip to main content
An Unmute package is a directory. The agent lives in agent.yaml, the prompt lives in a Markdown file next to it, and the target choice lives in targets.yaml. This page walks that file, key by key. Everything else in this section adds to it. Make one now, then read along in the file it writes. Already ran this in the quickstart? You are sitting inside that my-agent/ directory: cd .. first, or skip the command and read along in the file already on disk.
That package already validates and already runs. One agent, browser audio, one built-in tool so it can hang up, no phone number and no third-party account. Everything below is a tour of what is in it, so nothing here is a detour.
On this page:

The agent file

The small snippets below keep one block on screen at a time. Expand the full file when you want to see how they fit together. This is my-agent/agent.yaml as the scaffold wrote it, with its explanatory comments taken out so the shape is visible.
Now each block.

name

What this agent is called. The deployed name is this joined to the target it was compiled for, so on a target called livekit it deploys as my-agent-livekit. Required, lowercase letters, digits and single hyphens, and it must not be a name another package in your organisation already uses: a deploy replaces the agent whose name it matches. The name reference has the whole rule.

entry_agent

Which agent answers. A package can hold several agents; exactly one starts the call. The name has to be a key in the agents: map.

agents

An agent is a prompt plus the models it speaks and thinks with. instructions is a path to a Markdown file in the package. Writing the prompt in its own file means it reviews like prose, not like YAML.

secrets

A list of environment variable names. Never values. Unmute writes these into the generated .env.example and into a startup check inside the generated project, so a missing key stops the container with a clear message instead of failing on the first spoken word.
There is no way to write a secret’s value in the package, on purpose. See secrets.

models

Four kinds of model, grouped by what they do: Each section holds named entries. The name is yours: assistant_model, assistant_voice, transcriber, detector above. An agent then points at the entries it wants by name.
provider names the integration; model and voice are passed to that provider exactly as you wrote them. Unmute does not keep a list of valid model ids, so a typo shows up as a provider error at run time, not at compile time. params: is normally passthrough for anything else the provider takes. The one line here earns its place: gpt-5.6-terra is a reasoning model, and OpenAI refuses a chat request that carries function tools unless it sets reasoning_effort. This package has a tool, end_call, so the line is doing real work from the first run. The LiveKit Responses compiler directive is the narrow exception; Reasoning model has both forms.
You can define more entries than you use. Unused entries are legal alternates, which makes swapping a voice a one line change.
The listen and turn sections have one entry each here, so nothing needs to select them. With two or more entries you add a top level listen: or turn: line naming the one to use.

conversation

Who speaks first and what they say. The greeting is spoken word for word without going through the model, so changing it changes nothing else. conversation also holds interruption:, inactivity: and max_duration:, which the scaffold leaves out; the conversation reference has them.

channels and capacity

channels says how people reach this agent. web: realtime_audio is browser audio, which is what unmute dev serves. Phones come later, in Telephony. capacity is your traffic estimate. The compiler turns it into worker counts and quota numbers in the generated project, and marks them unbenchmarked because they come from a conservative assumption, not from a measurement of your agent.

The target file

targets.yaml
One target. Nothing in agent.yaml names a target, so this file is the only place the choice lives, and one package can declare several. A second instance here compiles a second complete project from the same agent, and a target that cannot run a model entry as written overrides that entry by name rather than changing the agent. Targets has both.

Try it

The scaffolded package validates clean. A warning, when you do get one, does not stop the command: it names a real difference worth reading. Now talk to the agent in your browser:

Agent fields

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.
string
required
Name of an entry in models.speak. No profile is inferred.
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 strings
Names from the top-level task_groups catalog. Omit for no groups.
list of strings
Names from the top-level handoffs catalog. Omit for no agent handoffs.
list of strings
Names from the top-level escalations catalog. Omit for no human transfers.

Where to go next

How a package fits together

One rule that gets you from this agent to several.

Variables

Personalize each call and pass values without exposing them to the model.

Agent configuration

Every supported agent.yaml key and value.