Skip to main content
SLNG hosts the agent. The compiler writes a deployment body and a runbook instead of Python. This page describes those files and the target’s settings.

Deploy to SLNG

Start here to create a package, select the organisation, supply credentials, and deploy or update the agent.
A typed local: tool works on this target. unmute deploy publishes it to SLNG as a code tool. A webhook: block is refused: create an API request tool in SLNG and reference it with slng:, or compile the package to pipecat or livekit, where your own endpoint runs. The rest of this page describes the target in full, including the tool shapes.
On this page:

The trade

You author prompts, models, tools, a greeting, and variables. SLNG runs the pipeline and owns its capacity and turn taking. There is no generated Python project or container to maintain. Some package features need a runtime you control. Check the target limits before choosing SLNG.

What compiling writes

To stop at the files:
The tools/ and samples/ folders exist only when the package has a local: tool. Every other tool reference in agent.json, whether a curated capability, a hosted tool, or an MCP server, resolves by name against your organisation. A slng: reference needs no mirror to reach even this point: agent.json carries the name, and nothing else about the tool.

A local tool on this target

unmute compile wraps the typed handler (handler: tools/<file>.py:<function>) into one SLNG code tool file. The file holds the handler module, the package’s state.py when the handler imports from it, and an adapter. The adapter defines Input from the function signature, Output from the return model, and handler(input) -> Output. Three rules apply to a local tool on this target:
  • Put a sample beside the tool file: tools/<name>.sample.json, one JSON object of arguments. SLNG publishes a code tool only after one run, so a local tool with no sample is refused.
  • Name the function in the handler, as file.py:function. A handler with no :function is refused. An async def handler is refused too, because SLNG calls a code tool synchronously.
  • Expect no internet. SLNG code has no network access. Exact dependency pins come from the tool’s dependencies:.
unmute deploy publishes the tool for you. See Deploy to SLNG and the deploy command. Validation and compilation run offline. Follow the deployment guide to install voiceai and push the generated body.

Targets YAML

targets.yaml
Write these inside targets.<name>. The name selects this target with --target and names its output folder under build/.
string
required
Use slng for this target. Omission is refused.
string or one-item list of strings
required
Accepts SLNG’s own regions us-east, us-west, br, eu-west, eu-north, gb, za, il, jp, sg, id, in, au, and the LiveKit Cloud regions us-east-livekit, eu-central-livekit and ap-south-livekit. Exactly one region is required; omission, an unknown name, and multiple regions are refused. The retired any value is refused. These are the agents API’s region names, not the world parts that speech and the router take, although SLNG’s own are spelled the same. us-east-livekit is LiveKit Cloud’s US East; SLNG’s own is us-east. The retired codes are refused with the code to use, as the agents API refuses them: LiveKit Cloud’s eu-central and ap-south, and SLNG’s us-east-slng. A package that names us-east for LiveKit Cloud, as packages did before the -livekit suffix, now deploys to SLNG’s own US East.
map of model definitions
Overrides keyed by existing model names from agent.yaml, using the model fields. Omit to use the package’s models. An override replaces the entry, except that omitted pace, endpointing_delay, semantic_endpointing, and prompt_suffix carry forward. An override cannot author pace or a different prompt_suffix.
string
No non-empty value is accepted. Omit it: SLNG owns the runtime version.
map of strings
No non-empty map is accepted. Omit it: there is no generated project whose dependencies could be pinned.
string
No non-empty value is accepted. Omit it: this target emits no SDK project.
string
No non-empty value is accepted. Omit it: configure the carrier route in SLNG and attach its trunk at deployment, as described in phone setup.
integer
Positive and negative values are refused. Omit it: SLNG owns capacity. Zero also emits no setting.

The pushed agent’s name

The deployed name joins the package’s name: to its target. A package named my-agent with target slng becomes my-agent-slng. Deployment resolves an existing agent by name, or uses --agent-id when supplied. Check the dry run before pushing. See updates and renames for how to keep or replace an existing deployment.

Model names

SLNG names a model with the vendor and the model joined by a slash. A package writes them as two fields, and the slng driver joins them when it writes the body:
agent.yaml
That reaches SLNG as one string. A model name that already carries a slash is passed through whole rather than joined twice, which is how a SLNG Context Router model such as slng/deepgram/nova:3-en reaches the body unchanged. Model availability depends on your organisation and the selected region. Follow Choose models and their region to check the available bindings before deployment.

Push it

Follow Deploy to SLNG to create the package, select an organisation, preview the changes, and push the agent. That guide also covers updates, Vault credentials, and testing a complete interaction. The generated build/slng/README.md lists your package’s requirements. The deploy command reference describes every flag. Use unmute deploy for checked deployment. Running voiceai agents push directly bypasses Unmute’s binding checks and resolved staging.

Where your tools come from

Reference published tools by name with slng:. This target needs no local mirror. A package that also targets LiveKit or Pipecat needs unmute pull, because those targets run a local copy of the tool.

Names, not identifiers

agent.json writes each tool and MCP reference as a name, because no compiler can invent an identifier a server assigns. SLNG’s tool_refs entries require attachment_id, tool_id and version, so a name has to become an id before the body is accepted. This includes a curated capability. A builtin: tool needs nothing created, because SLNG already has it, and it still has a tool_id that has to be filled in. Resolving those names is the push step’s job. It is also why voiceai agents create --file build/slng/agent.json is the wrong command: that posts the body verbatim, names included, and the API refuses it. A webhook: tool never reaches this step: the block is refused before compiling, for the reason at the top of this page. A local: tool is published first. unmute deploy creates or updates it as a code tool, runs it once on its sample, and publishes it. Then this step attaches it by id and version like any other tool. An MCP reference resolves the same way, and it needs one thing agent.yaml has to say up front: which tools you want. Unmute compiles offline, so it cannot expand “every tool on the server” into a reference list; an mcp: tool with no mcp.tools list is refused at validate, naming the tool and pointing at mcp.tools. Once the list is there, the push looks up the server’s name for its server_id, then copies each named tool’s schema hash out of the platform’s own stored capability snapshot. A real unmute deploy can refresh an unusable snapshot once through voiceai mcp run <server>, then checks it again. A dry run never refreshes it and reports what needs attention. Discovery does not execute business tools. The runbook says which case your package is in.

A push replaces

Updates replace the agent with what the package declares, including its tool attachments and its tasks. A package with no tasks removes tasks somebody built in the dashboard. Preview the changes with unmute deploy --dry-run; follow Every later deploy for the full sequence.

Tasks and flows

Tasks and flows compile to SLNG’s own tasks, with the same meaning they have on LiveKit and Pipecat:
  • a task’s when:, prompt, tools, save:, one start: say: line, start: listen: true, an outcome with then: done, and a context.history of messages, full, last_n or reset
  • a flow’s steps, skip_when_confirmed, context_scope and then: return
  • confirm: on a variable, for a task with its own when:
  • a typed state.py value that a task saves, including a model, a list, a Literal, a phone number, an email address, a date or a time
A task names its tools by the names in tool_refs, and the push writes SLNG’s attachment ids in their place. A tool that only a task lists is attached hidden from the main agent. Every value a task saves becomes a runtime variable, so its Field in state.py needs a description=.

Do not push a dashboard export back

A dashboard export is not a deployment request. Compile from the authored package, then use unmute deploy to resolve and push the body.

Hosted tool dependencies

SLNG installs the dependencies declared by the published tool. An SLNG-only package needs no local dependency list. LiveKit and Pipecat refuse hosted tools that declare Python dependencies. See Hosted tools before sharing a package across targets.

The Vault

SLNG reads secrets from its own store. With your consent, unmute deploy can fill missing Vault entries through voiceai. The runbook lists what compiling the package could see, grouped by source, above the push command. No secret value reaches any emitted file or any command in the runbook. That list is only what a compile can read offline: a tool’s declared auth:, and anything a committed mirror recorded. A hosted tool or MCP server’s own credential is not on it, because SLNG holds that credential and a compile never asks SLNG anything. unmute deploy reads the account directly and may ask to create an entry the runbook never named. Two kinds appear there:
  • a secret is a credential a tool authenticates with, named in the package as an environment variable and stored in the Vault under the same name;
  • a variable is a {{$NAME}} token that SLNG substitutes at run time.
SLNG substitutes a {{$NAME}} token into an MCP server URL and nowhere else. A prompt and a greeting are templates whose placeholder names hold letters, digits and underscores, so a token written in either is refused when the agent is pushed. Unmute refuses it at validation instead, on every target, and the message says what the token is rather than telling you to declare a variable that must not exist. For a value your prompt varies per call, declare a package variable and write {{"{{state.name}}"}}. For a credential, let the tool that needs it name its Vault entry: a tool’s auth: block names the entry by its bare name, with no braces and no dollar. A package that needs no Vault entries is told so, rather than shown an empty list.

What a slng package may not ask for

Every one of these is refused at validate, by name, with what to do instead. None is dropped quietly. A package with a state.py needs uv on PATH to validate and compile, even when SLNG is its only target, because unmute reads the file by running it. The first run needs network. A package without a state.py needs no uv. An MCP tool works when its selected names and discovery snapshot pass the platform checks. A real deploy may refresh an unusable snapshot once; a dry run reports it without refreshing.

If the agent answers with silence

A successful deployment does not prove that each provider answers during a call. Follow Verify the agent to test an interaction and Troubleshooting to inspect a failure.

There is no unmute dev

unmute dev runs a generated project locally; this target emits no project. Use the SLNG dashboard to test the deployed agent, or create a session for your own client.

Where to go next

Speech to text

Which vendors each target accepts, and how SLNG fits in.

Deploy to SLNG

The walkthrough: what you need first, and every failure message.