Clone once, then send the same voice_id to synthesis and to a live session so your whole product speaks in one voice.

Before you start

The clip is the conditioning — there is no training run to average anything away, so whatever is in it is inherited. Room reverb clones the room, background music clones a hum, a second voice is a voice the model may reach for. Longer is not better; cleaner is. Up to 10 MB, in any of wav, mp3, m4a, mp4, flac, ogg, opus, aac or webm. See Voice cloning.
Cloning costs a flat 100 credits ($1.00). An organization that has never bought credits gets exactly one — still charged the fee, which the signup grant covers — and a second answers 403 plan_required until you top up. See Pricing and limits.
The request carries consent_attested, and it is checked before the upload is even read: audio you have no right to clone is audio the platform never takes. Attesting means the voice is yours, or you hold the speaker’s written consent. See Compliance.

1. Clone it

Multipart. file, name and consent_attested are the ones that matter; everything else is metadata your library filters on.
ref_text is what the clip actually says. It is optional, and worth giving: the library shows it, so a voice can be judged on the take it was built from, and two clones can be seen to have been read the same line. Omit consent_attested and you get a 400 naming the field rather than a bare “field required” — it is defaulted specifically so the refusal can say what you are attesting to.

2. Wait for it to be ready

The clone comes back processing, not ready. Preparation runs as a background job, and using the voice before it finishes is a 400 naming the status.
Note the .value in the Python. VoiceResponse.status is a plain Enum member, not a string, so voice.status == "processing" is False for a clone that is processing. Written without it the loop never runs, the != "ready" below is true on the first pass, and every successful clone raises clone failed: None after zero polls. Compare voice.status.value, or import Status from eesi_sdk.models and compare to the member. The TypeScript field is a "processing" | "ready" | "failed" union and compares to a string directly.

3. Audition it before you ship it

POST /v1/voices/{voice_id}/preview synthesizes a fixed sentence in the cloned voice and returns mp3. One listen tells you whether you have a clip problem or a model problem.
A preview is ordinary synthesis: it costs its seconds, spends from the per-minute request budget, and counts against the free-tier daily ceiling like any other generation. If the preview is wrong, the clip is almost always the reason — re-record rather than re-clone the same take. GET /v1/voices/{voice_id}/sample gives you back a short-lived URL for the reference audio so you can listen to what the model was actually given.

4. Use the same id everywhere

This is the point of the exercise. One voice_id, two surfaces, the same speaker.
In a live session the voice goes in session.audio.output.voice and nowhere else. There is no top-level voice on this session shape, and no query parameter for it.
The retired omni realtime id did not accept cloned voices. Its adapter sent no reference audio upstream, so an ev_… id there has no effect rather than raising an error — the session answers in the model’s own voice and nothing says why. Check supports_cloning on GET /v1/audio/models before you send a cloned voice to a realtime model. See Models.

Managing the library

name is the only field PATCH changes. VoiceUpdateRequest carries nothing else, so a category, description or gender in that body is dropped: you get 200 OK and the row is unchanged. Everything but the name describes the recording, and a voiceprint whose reference audio could be swapped after consent was attested would make the attestation meaningless. Category is fixed at creation — to recategorize, clone the clip again under the category you want. PATCH on a built-in voice is a 400. Built-in voices are the curated global library and cannot be deleted by a tenant. Deleting a clone does not hand back the free-plan allowance — that is counted from the ledger, so the clone you made and removed is still the clone you used. Every voice the API returns carries consent_attested_at, which is when somebody attested to the right to clone it. It is null for built-ins, and for clones made before the attestation was required.

Why the voice holds across requests

Every voice — built-in or cloned — resolves to the same stored reference clip on every request. That is why part twelve of a narration sounds like part one, and why a two-hour call does not change speaker halfway. Without reference audio the synthesis model samples a new voice per generation. It is also why a voice is a resource you create rather than a string you pass, and why an unrecognized voice name is a 400 unknown_voice rather than audio in the wrong voice: the backend would otherwise drop the name it did not recognize and return 200 OK in its default voice, which is the hardest failure on this route to diagnose.

Next

Voice cloning

What makes a clip good, and how to hear a bad one.

Voices

The full resource, and where a voice id is read.

Stream speech to a file

Narrate something long in the voice you cloned.

Compliance

What the consent attestation actually asserts.