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. Speech-to-speech is not transcription followed by synthesis — the model listens to the audio itself, so it hears tone, hesitation, and emphasis.
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
Setlanguage 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 — it skips detection (lower latency, better 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
Withmodel: "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.
Constraining the route
The optionalprovider object narrows and orders candidates for a single request:
Transcription
Key parameters
Multipart requests take the same fields as form values (
provider, metadata, options, and keyterms as JSON-encoded strings). Upload limits differ by transport: multipart accepts up to 100 MB; the base64 JSON body caps at ~18 MB of source audio (25 MB request limit). The SDK and CLI use the JSON transport, so upload larger recordings via multipart or downsample first.
Long recordings
Add?async=true to queue the job. You get a 202 with a request id instead of holding the connection open:
queued → running → succeeded (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.
Speech synthesis
The response is binary audio in the requestedresponse_format, streamed straight through.
The upstream request id is returned on the
X-Audio-Request-Id header — use it with GET /v1/audio/requests/{id} (route trace) or GET /v1/audio/usage/{id} (cost), since a binary body carries no 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.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: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
Theaudio 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.