Skip to main content
The audio primitive gives your agent ears and a voice. One endpoint covers all three speech modalities, routed automatically across a managed catalog of speech models: You never manage a speech-provider account or key. Naive holds the credentials, picks the model, and bills each call in Naive credits from the exact cost the request reports.
Speech-to-speech is not transcription followed by synthesis. The model listens to the audio itself, so it hears tone, hesitation, and emphasis that a transcript throws away.

CLI First

Endpoints

Routing

Every modality takes either a managed alias or an exact model slug. Managed routes enable automatic fallbacks; a pinned model does not retry elsewhere unless you opt in with provider.allow_fallbacks. Responses carry a route object explaining what was considered, filtered, and attempted:

Route by language

Set language and leave model unset. The request goes to the best model for that language, and you never name a model yourself.
Always send language when you know it. On a managed route it skips language detection entirely — that lowers latency on every request, and the model transcribes against the language you specified instead of guessing, which improves accuracy. Use a BCP-47-style code (de, es, pt-BR); a region tag with no dedicated model falls back to its base language.

Steering speech synthesis

With model: "tts/auto" and voice: "auto" you can name the capability that matters and let Naive rank models for it. Omit feature and it is inferred from your text, instructions, language, and length. Speech-to-speech has its own set: Emotion Understanding, Emotion Alignment, Expressive Robustness, Voice naturalness, and Problem redirecting.
feature values are case-sensitive, and valid only on tts/auto / s2s/auto with voice: "auto". Sending one with a pinned model is rejected — native voice ids aren’t portable across models, so a managed route can’t honor them.

Constraining the route

The optional provider object narrows and orders candidates for a single request:
Requests are pinned to ["us", "global"] unless you say otherwise. The model catalog spans regions including cn-beijing, and a managed alias would otherwise be free to pick any of them. Naive applies this residency floor to every request that doesn’t set provider.region; it costs 3/83 transcription, 5/54 speech, and 2/11 s2s endpoints.Setting provider.region yourself replaces the default entirely — it’s a default, not a ceiling — so pass ["eu"] (or a wider list) when you need different residency.
Speech-to-speech cannot be residency-pinned. That route rejects the whole provider preference vocabulary — region, only, ignore, order, and allow_fallbacks all return invalid_input / Unrecognized key. Naive therefore does not apply the residency floor to POST /v1/audio/speech-to-speech.The practical consequence: 2 of the 11 audio-turn models (qwen/qwen3-omni-flash and qwen/qwen3-omni-flash-realtime) serve ap-southeast-1 and cn-beijing, and s2s/auto may select them. If you need a residency guarantee on an audio turn, pin an exact model slug — for example openai/gpt-realtime or deepgram/realtime — instead of using s2s/auto.

Transcription

Response:

Key parameters

Multipart requests take the same fields as form values, but provider, metadata, options, and keyterms must be JSON-encoded strings. Booleans and numbers are coerced for you.
Upload limits differ by transport. A multipart upload accepts up to 100 MB. The base64 JSON body goes through the API’s JSON parser, which caps requests at 25 MB — about 18 MB of source audio once base64 inflates it. The SDK and CLI use the JSON transport, so upload larger recordings via multipart, or downsample first (ffmpeg -i in.wav -ac 1 -ar 16000 out.wav is plenty for transcription).

Long recordings

Add ?async=true to queue the job. You get a 202 with a request id instead of holding the connection open:
Status moves through queuedrunningsucceeded (or failed / canceled). Result fields stay null until it succeeds, and the credit charge lands on the first poll that sees succeeded — polling again never double-charges.
Do not set retention: "none" on an async transcription. The result is fetched later, so a job queued with none completes as succeeded with text: null — you are billed and the transcript is gone.Naive therefore defaults async requests to retention: "transcript" (the minimum that makes polling work) and sync requests to retention: "none" (the transcript is already in your response, so nothing needs storing). Setting retention yourself always wins, including setting it to none — so only do that on async if you genuinely don’t want the result.

Speech synthesis

The response is binary audio in the requested response_format, streamed straight through.
Naive returns the upstream request id on the X-Audio-Request-Id response header. Use it with GET /v1/audio/requests/{id} for the route trace, or GET /v1/audio/usage/{id} for the cost — a binary body has nowhere to put a usage object.
Supported formats vary by model. Check GET /v1/audio/endpoints for a model’s limits.formats before pinning one.

Audio conversations

Send one spoken turn and get a spoken reply, plus both transcripts.
Response:

Catalog & observability

GET /v1/audio/endpoints is the detailed view: per-endpoint capability flags (diarization, word_timestamps, stream, redaction, translation, …), price unit and rate, accepted formats, and byte/duration limits. Use it when you need to pin a model and want to check it can actually do the job first.

Multi-tenant

Like every primitive, the routes are AccountKit-gated and available per-user:
audio is opt-in and disabled by default. Enable it per Account Kit in the dashboard (Account Kits → Primitives → Generation), or set primitives_config.audio.enabled = true. Until you do, the routes return 403 forbidden. The reason tells you which gate fired: primitive_disabled_by_kit on a kit that has audio seeded off (the default for any workspace provisioned after this primitive shipped), or subprocessor_consent_required on an older kit that has no audio entry at all — opt-in primitives are denied on an absent entry, not just an explicit false.It is opt-in because the primitive sends tenant voice to a third-party router that fans out to 30+ speech providers, selected per request. Which provider handled a given utterance is only knowable after the fact, from the route trace on the response. Treat enabling it as granting subprocessor consent — the same standard as Brain. See Account Kits.

Billing

Naive bills the exact cost each request reports, times a small markup, converted to credits ($0.05 = 1 credit) — the same model as LLM, with no per-model rate table to keep in sync. Catalog, usage, and route-trace reads are free. Async transcriptions are charged once, when the job completes. Very short calls can round below credit precision and cost nothing at all. See Credits.

Agent tools

The audio primitive is part of agentTools(). The model can call naive_run_primitive(primitive: "audio", method: "transcribe" | "transcription" | "converse" | "models", arguments: { ... }). Synthesis is intentionally not exposed as an agent tool: it returns raw audio bytes, which have no useful representation in a model’s context. Call POST /v1/audio/speech (or naive.audio.speech()) directly and write the bytes to a file or a signed URL.

Error Handling