> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Audio

> Speech to text, text to speech and speech to speech — transcribe a recording into a timestamped transcript, turn text into a stored audio file, or answer one spoken turn with another.

Three directions, three routes. **Transcription** takes a recording — a stored [file](https://vetta.sh/docs/api/files.md) or a public URL — and answers a transcript with per-segment timestamps. **Speech** takes up to 4 096 characters of text and answers an audio file, already saved to [Files](https://vetta.sh/docs/api/files.md) so the same `fil_` can be downloaded, published or handed to an agent. **Conversation** takes a stored recording of one spoken turn and answers the spoken reply, saved the same way — one model listens and speaks, with no transcript or text model in between.

Alongside the three, **[Sound effects](#sound-effects)** turns a text prompt into a short generated clip — ambience, foley, a sting — and **[Music](#music)** turns one into an original song, both saved the same way. All of them bill on the `media` component, what the finished call actually cost, exactly like [image generation](https://vetta.sh/docs/platform/pricing.md#media-generation--per-finished-job-media-component). Inside a session, transcription, speech, sound effects and music are the [`transcribe_audio`, `generate_speech`, `generate_sound_effect` and `generate_music`](https://vetta.sh/docs/capabilities/tools#audio-tools) tools; there is no conversation tool.

## Managed aliases and pinned models

Every route takes an optional `model`. Omit it and the request runs on the direction's **default** — the first row of its [catalogue](#the-catalogues); for speech that is the [self-hosted default model](#speak), which hands a line it cannot speak to the next model. Name the **managed alias** for a direction — `stt/auto`, `tts/auto`, `s2s/auto` — to let the platform pick the model for each request, the one voice mode uses; it falls back automatically when one is unavailable. Name a pinned `owner/model` from the catalogue to run on exactly that model instead; a pinned model that is unavailable fails rather than falling back. The reply's `model` names the model that actually ran.

Transcription and speech run in a **data-residency floor** by default: recordings and text are processed in the United States or on globally-hosted endpoints, never in a region-restricted deployment. Speech-to-speech models are hosted by their own maker, so a conversation has no such floor — the reply comes from wherever the model you chose runs.

## The transcription job

A transcription is a [`media_job`](https://vetta.sh/docs/api/media) of `kind: "transcription"`. Most recordings are transcribed inside the request, so the job comes back `completed` on the `202` itself. A long recording may be **queued** upstream: the job then comes back `processing`, with the same shape, and the platform keeps collecting it in the background — re-read it with `GET /v1/media/audio/transcriptions/{id}` until it is `completed` or `failed`. `cost_micro_usd` is `null` until the transcript lands; the debit is booked once, when it does.

The audio routes emit no `media.job.*` [events](https://vetta.sh/docs/api/events.md).

**id** `string`

`med_…`

**object** `string`

Always `media_job`.

**kind** `string`

Always `transcription`.

**status** `string`

`queued` · `processing` · `completed` · `failed`.

**model** `string`

The transcription model or alias that ran.

**input** `object`

What you sent, minus `model`.

**result** `object | null`

Set once `completed`:

**fields**

**text** `string`

The whole transcript.

**language** `string | null`

The language detected, ISO 639-1, when the model reports one.

**duration\_seconds** `number | null`

The recording's length, when the model reports it.

**segments** `array`

`{ start_seconds, end_seconds, text }` per utterance. Empty when `timestamps: false`.

**error** `object | null`

`{ code, message }` when `failed`.

**cost\_micro\_usd** `integer | null`

What was booked, in micro-USD; `null` until the job completes.

**created\_at** `string`

RFC 3339.

**completed\_at** `string | null`

RFC 3339.

## Transcribe

`POST /v1/media/audio/transcriptions` — scope `agents:write`, [`Idempotency-Key`](https://vetta.sh/docs/api/overview#idempotency) required

**file\_id** `string`

A stored audio file, `fil_…`. **Exactly one** of `file_id` or `url`.

**url** `string`

A public `http(s)` URL of an audio file, at most **100 MiB**. **Exactly one** of `file_id` or `url`. The same limits as a [file import](https://vetta.sh/docs/api/files.md#import-a-file-from-a-url): loopback, private-network, link-local and cloud-metadata hosts are refused, every redirect is checked the same way, and a larger declared or actual body is `validation_failed` on `url`.

**model** `string` (default: stt/auto)

A model from [`GET /v1/media/models?kind=stt`](#the-catalogues).

**language** `string`

ISO 639-1 hint, for example `en`. Omitted, the model detects it.

**timestamps** `boolean` (default: true)

Return per-segment timestamps.

```bash cURL theme={"system"}
curl -fsSL https://api.vetta.sh/v1/media/audio/transcriptions \
  -H "authorization: Bearer sk_live_..." \
  -H "idempotency-key: 4a1f…" \
  -H "content-type: application/json" \
  -d '{ "url": "https://cdn.example.com/earnings-call.mp3", "language": "en" }'
```

```ts SDK theme={"system"}
const job = await vetta.media.audio.transcribe({
  url: "https://cdn.example.com/earnings-call.mp3",
  language: "en",
});
```

```bash CLI theme={"system"}
vetta media transcribe https://cdn.example.com/earnings-call.mp3 --language en
```

```json 202 Accepted theme={"system"}
{
  "id": "med_01j9x3c2g8h4k5m6n7p8q9r0s1",
  "object": "media_job",
  "kind": "transcription",
  "status": "completed",
  "model": "stt/auto",
  "input": { "url": "https://cdn.example.com/earnings-call.mp3", "language": "en", "timestamps": true },
  "result": {
    "text": "Good morning, and thank you for joining us.",
    "language": "en",
    "duration_seconds": 3.4,
    "segments": [{ "start_seconds": 0, "end_seconds": 3.4, "text": "Good morning, and thank you for joining us." }]
  },
  "error": null,
  "cost_micro_usd": 184,
  "session_id": null,
  "created_at": "2026-09-06T10:00:00Z",
  "completed_at": "2026-09-06T10:00:00Z"
}
```

## Retrieve a transcription

`GET /v1/media/audio/transcriptions/{id}` — scope `agents:read`

The same object, by id. A `med_` from another organization, or one that is not a transcription, is `404 not_found`.

## Speak

`POST /v1/media/audio/speech` — scope `agents:write`, [`Idempotency-Key`](https://vetta.sh/docs/api/overview#idempotency) required

Synchronous: the reply carries the stored file. Download the bytes with [`GET /v1/files/{id}?download=true`](https://vetta.sh/docs/api/files#retrieve-a-file).

**text** `string` *required*

What to say, 1–4 096 characters.

**model** `string`

A model from [`GET /v1/media/models?kind=tts`](#the-catalogues), or `tts/auto`. Omitted, the default model below.

**voice** `string`

One of the voices the model offers. Omitted, the model's default. A voice the model does not have is `validation_failed` on `voice`.

**format** `string` (default: mp3)

`mp3` · `wav` · `ogg`.

**speed** `number`

0.5–2. Omitted, the model's own pace.

**The default speech model** is `hexgrad/kokoro-82m`, an open model we run on our own servers — the cheapest voice in the catalogue at \*\*$0.000029 per second of audio** ($0.00174 a minute). It speaks **English only**, in 28 voices: American `af_alloy` `af_aoede` `af_bella` `af_heart` (the default) `af_jessica` `af_kore` `af_nicole` `af_nova` `af_river` `af_sarah` `af_sky` `am_adam` `am_echo` `am_eric` `am_fenrir` `am_liam` `am_michael` `am_onyx` `am_puck` `am_santa`, and British `bf_alice` `bf_emma` `bf_isabella` `bf_lily` `bm_daniel` `bm_fable` `bm_george` `bm_lewis`; `alloy`, `echo`, `fable`, `nova` and `onyx` pick their namesakes. When you omit `model`, a request it cannot serve — another voice name, text in a non-Latin script or a sentence full of accented letters, a line that would take it more than about 90 seconds to speak (roughly 1 500 characters or more), or the model being busy or briefly unavailable — is spoken by the next model in the catalogue instead, and the reply's `model` says which one spoke. Text in another language with few or no accented letters cannot be told apart from English, so name a multilingual model for it. Pin `hexgrad/kokoro-82m` to never switch: an unknown voice is then `validation_failed` and an outage `provider_error`.

```bash cURL theme={"system"}
curl -fsSL https://api.vetta.sh/v1/media/audio/speech \
  -H "authorization: Bearer sk_live_..." \
  -H "idempotency-key: 9c7e…" \
  -H "content-type: application/json" \
  -d '{ "text": "Your order has shipped and arrives Thursday." }'
```

```ts SDK theme={"system"}
const speech = await vetta.media.audio.speak({ text: "Your order has shipped and arrives Thursday." });
const mp3 = await vetta.files.downloadBytes(speech.file.id);
```

```bash CLI theme={"system"}
vetta media speak "Your order has shipped and arrives Thursday." | jq -r .file.id | xargs vetta file download > shipped.mp3
```

```json 200 OK theme={"system"}
{
  "object": "speech_generation",
  "model": "hexgrad/kokoro-82m",
  "file": {
    "id": "fil_01j9x3d7k2m4n6p8q0r2s4t6u8",
    "object": "file",
    "name": "fil_01j9x3d7k2m4n6p8q0r2s4t6u8.wav",
    "content_type": "audio/wav",
    "size_bytes": 134444,
    "scope": "published",
    "session_id": null,
    "sha256": "3f…",
    "created_at": "2026-09-06T10:00:01Z"
  },
  "duration_seconds": 2.8,
  "cost_micro_usd": 82
}
```

The default model's cost is its seconds of audio at the rate above, rounded up to a whole micro-dollar. Another model's cost is settled from the provider's usage record after the audio is returned. When that record is not yet available, the call is billed at the `media` component's floor rather than held.

## Converse

`POST /v1/media/audio/conversations` — scope `agents:write`, [`Idempotency-Key`](https://vetta.sh/docs/api/overview#idempotency) required

Synchronous, like speech: one spoken turn in, the spoken reply out, as a stored file. The input is a stored file only — [upload](https://vetta.sh/docs/api/files.md#upload-a-file) or [import](https://vetta.sh/docs/api/files.md#import-a-file-from-a-url) a recording first; the CLI does both for you.

**file\_id** `string` *required*

A stored audio file, `fil_…`, holding the turn to answer.

**model** `string` (default: s2s/auto)

A model from [`GET /v1/media/models?kind=s2s`](#the-catalogues).

**voice** `string`

One of the voices the model offers. Omitted, the model's default.

**format** `string` (default: mp3)

`mp3` · `wav` · `ogg`, the format of the reply.

```bash cURL theme={"system"}
curl -fsSL https://api.vetta.sh/v1/media/audio/conversations \
  -H "authorization: Bearer sk_live_..." \
  -H "idempotency-key: b81d…" \
  -H "content-type: application/json" \
  -d '{ "file_id": "fil_01j9x3f0a1b2c3d4e5f6g7h8j9", "format": "wav" }'
```

```ts SDK theme={"system"}
const reply = await vetta.media.audio.converse({ file_id: turn.id, format: "wav" });
const wav = await vetta.files.downloadBytes(reply.file.id);
```

```bash CLI theme={"system"}
vetta media converse ./question.wav | jq -r .file.id | xargs vetta file download > reply.mp3
```

The reply is a `speech_generation`, the same object [Speak](#speak) answers, with `model` naming the conversation model or alias that ran.

## Sound effects

`POST /v1/media/sound-effects` — scope `agents:write`, [`Idempotency-Key`](https://vetta.sh/docs/api/overview#idempotency) required

Synchronous, like speech: a text prompt in, a short generated clip out, saved to [Files](https://vetta.sh/docs/api/files.md) as a `fil_`. This is not speech — it turns a description (`"a heavy wooden door slamming shut, echoing"`) into an effect — so it takes no `voice` and no `model`; the reply names none. Inside a session it is the [`generate_sound_effect`](https://vetta.sh/docs/capabilities/tools#audio-tools) tool.

**prompt** `string` *required*

What to generate, 1–500 characters.

**duration\_seconds** `number`

How long the clip should be, 0.5–30. Omitted, the generator picks a length to fit the prompt.

**loop** `boolean` (default: false)

Make the clip loop seamlessly — for ambience and backgrounds.

```bash cURL theme={"system"}
curl -fsSL https://api.vetta.sh/v1/media/sound-effects \
  -H "authorization: Bearer sk_live_..." \
  -H "idempotency-key: 2d6a…" \
  -H "content-type: application/json" \
  -d '{ "prompt": "a heavy wooden door slamming shut, echoing", "duration_seconds": 3 }'
```

```ts SDK theme={"system"}
const sound = await vetta.media.soundEffects.generate({
  prompt: "a heavy wooden door slamming shut, echoing",
  duration_seconds: 3,
});
const mp3 = await vetta.files.downloadBytes(sound.file.id);
```

```bash CLI theme={"system"}
vetta media sound-effect "a heavy wooden door slamming shut, echoing" | jq -r .file.id | xargs vetta file download > door.mp3
```

```json 200 OK theme={"system"}
{
  "object": "sound_generation",
  "file": {
    "id": "fil_01j9x3g4m6p8q0r2s4t6u8v0w2",
    "object": "file",
    "name": "fil_01j9x3g4m6p8q0r2s4t6u8v0w2.mp3",
    "content_type": "audio/mpeg",
    "size_bytes": 48192,
    "scope": "published",
    "session_id": null,
    "sha256": "7a…",
    "created_at": "2026-09-06T10:00:02Z"
  },
  "duration_seconds": 3,
  "cost_micro_usd": 7800
}
```

The cost is settled from the clip the generator returns — its length in seconds, never more than 30 — after the audio lands, billed once on the `media` component under the `Idempotency-Key`.

## Music

`POST /v1/media/music` — scope `agents:write`, [`Idempotency-Key`](https://vetta.sh/docs/api/overview#idempotency) required

Synchronous, like sound effects: a text prompt in, an original song out, saved to [Files](https://vetta.sh/docs/api/files.md) as a `fil_`. Describe the genre, mood, instrumentation and any lyrics (`"an upbeat synthwave track with driving bass and bright arpeggios"`); it takes no `voice` and no `model`, and the reply names none. Inside a session it is the [`generate_music`](https://vetta.sh/docs/capabilities/tools#audio-tools) tool.

**prompt** `string` *required*

What to generate, 1–2 000 characters.

**length\_seconds** `number`

How long the song should be, 3–300. Omitted, 30.

**instrumental** `boolean` (default: false)

Force an instrumental track with no vocals.

```bash cURL theme={"system"}
curl -fsSL https://api.vetta.sh/v1/media/music \
  -H "authorization: Bearer sk_live_..." \
  -H "idempotency-key: 4f8c…" \
  -H "content-type: application/json" \
  -d '{ "prompt": "an upbeat synthwave track with driving bass and bright arpeggios", "length_seconds": 30 }'
```

```ts SDK theme={"system"}
const song = await vetta.media.music.generate({
  prompt: "an upbeat synthwave track with driving bass and bright arpeggios",
  length_seconds: 30,
});
const mp3 = await vetta.files.downloadBytes(song.file.id);
```

```bash CLI theme={"system"}
vetta media music "an upbeat synthwave track with driving bass and bright arpeggios" | jq -r .file.id | xargs vetta file download > track.mp3
```

```json 200 OK theme={"system"}
{
  "object": "music_generation",
  "file": {
    "id": "fil_01j9x3g4m6p8q0r2s4t6u8v0w2",
    "object": "file",
    "name": "fil_01j9x3g4m6p8q0r2s4t6u8v0w2.mp3",
    "content_type": "audio/mpeg",
    "size_bytes": 481920,
    "scope": "published",
    "session_id": null,
    "sha256": "9c…",
    "created_at": "2026-09-06T10:00:30Z"
  },
  "duration_seconds": 30,
  "cost_micro_usd": 97500
}
```

The cost is settled from the song the generator returns — its length in seconds, never more than 300 — after the audio lands, billed once on the `media` component under the `Idempotency-Key`.

## The catalogues

[`GET /v1/media/models`](https://vetta.sh/docs/api/media) answers the audio models too: `kind=stt`, `kind=tts` and `kind=s2s` each list the managed alias for that direction first, then the pinned models cheapest-first where a published price exists. Each row's `description` carries the published price per second or per request where there is one. Voices are the model's own: a speech or conversation model accepts the voice names its maker publishes, and the managed aliases pick a voice when none is named.

## Errors

| Status | Code | When |
| - | - | - |
| `402` | `insufficient_credits` | The prepaid balance is empty. Nothing is sent to the provider. |
| `404` | `not_found` | `file_id` names no file of yours; `GET` names no transcription of yours. |
| `400` | `validation_failed` | Both or neither of `file_id`/`url`; a `url` at a private host, over 100 MiB or that does not answer `2xx`; `text` over 4 096 characters; a sound-effect `prompt` over 500 characters or a `duration_seconds` outside 0.5–30; a music `prompt` over 2 000 characters or a `length_seconds` outside 3–300; a `voice` the model does not offer; a `model` the catalogue does not have; audio the model cannot read. `param` names the field. |
| `429` | `rate_limited` | The provider is throttling. Retry after a moment with the same `Idempotency-Key`. |
| `501` | `feature_not_configured` | This deploy has no audio provider configured, or its account can no longer be billed. |
| `502` | `provider_error` | The provider failed. Retry with the same `Idempotency-Key`. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.