Skip to content
Console

Speech

Voices

Copy page

List the voices you can speak with, clone one from a recording, design one from a description, and edit or delete your own.

A voice is what voice_id names on text to speech, stored audio and an agent. You can speak with three kinds:

kind What it is Who can change it
builtin default, a female English voice. Always available, even when nothing else is. nobody
platform Voices the platform provides to every organisation. nobody, through the API
org Your organisation’s own: cloned from a recording, or designed from a description. Only your organisation sees them. you, with voices:write

Reading needs tts; changing needs voices:write. A key that may speak cannot delete the voices your organisation made. See Authentication.

curl
curl https://api.lumavox.ai/v1/voices \
-H "Authorization: Bearer $API_KEY"
Response
{
"data": [
{
"id": "default",
"name": "Default",
"description": "Female English voice (ElevenLabs' Sarah). Built into the platform; always available.",
"kind": "builtin",
"status": "ready",
"duration_seconds": null,
"provider": "elevenlabs",
"models": ["eleven_flash_v2_5", "eleven_multilingual_v2", "eleven_v3"],
"samples": [],
"created_at": null
},
{
"id": "voi_q1W2e3R4t5Y6u7I8",
"name": "Narrator",
"description": "Warm, measured, British.",
"kind": "org",
"status": "ready",
"duration_seconds": 42.7,
"provider": "elevenlabs",
"models": ["eleven_flash_v2_5", "eleven_multilingual_v2", "eleven_v3"],
"samples": [
{"model": "eleven_flash_v2_5", "ready": true, "error": null, "url": "https://…"},
{"model": "eleven_multilingual_v2", "ready": true, "error": null, "url": "https://…"},
{"model": "eleven_v3", "ready": false, "error": null, "url": null}
],
"created_at": "2026-10-20T14:02:11.000Z"
}
],
"next_cursor": null
}
  • Every voice is in one page: next_cursor is always null (Conventions). The built-in voice is always first.
  • id is what you send as voice_id. name is a label and is not unique.
  • status is ready for every voice that can speak; only a voice your organisation is adding can be pending or failed.
  • models is what the voice can be spoken with — send one of them as model.
  • samples holds one spoken sample per model, generated or not yet. A sample’s url is temporary: read it from a fresh response rather than storing it.
  • duration_seconds is the length of the recording a voice was cloned from, and null for one that was not.

GET /v1/voices/{voice_id} returns one of your organisation’s voices in full. A platform voice or the built-in voice is a 404 here; they are in the list.

curl
curl https://api.lumavox.ai/v1/voices/voi_a1B2c3D4e5F6g7H8 \
-H "Authorization: Bearer $API_KEY"
status Meaning
pending Being cloned. It cannot speak yet.
ready Usable.
failed The clone failed. error says why. Retry it, or delete it.
Response
{
"id": "voi_a1B2c3D4e5F6g7H8",
"name": "Narrator",
"description": "Warm, measured, British.",
"kind": "org",
"status": "ready",
"error": null,
"source": "upload",
"design_prompt": null,
"duration_seconds": 42.7,
"samples": [
{"model": "eleven_flash_v2_5", "ready": true, "error": null, "url": "https://…"},
{"model": "eleven_multilingual_v2", "ready": true, "error": null, "url": "https://…"},
{"model": "eleven_v3", "ready": true, "error": null, "url": "https://…"}
],
"provider": "elevenlabs",
"external_voice_id": "pNInz6obpgDQGcFmaJgB",
"models": ["eleven_flash_v2_5", "eleven_multilingual_v2", "eleven_v3"],
"provider_defaults": {
"stability": 0.6,
"similarity_boost": null,
"style": null,
"use_speaker_boost": null,
"speed": null
},
"created_at": "2026-10-20T14:02:11.000Z",
"created_by_clerk_user_id": null,
"created_by_api_key_id": "0193a8c2-51f0-7d2e-8b6a-4c1e9f3d2a70"
}

Beyond the list’s fields, the detail has error, source (upload or design), design_prompt (what a designed voice was described as), external_voice_id (the provider’s own id, null until it is cloned), provider_defaults (the settings it speaks with unless a request overrides them, each null where it keeps the provider’s) and who added it — created_by_clerk_user_id for a person in the console, created_by_api_key_id for a key, the other null.

Cloning takes three calls: ask where to upload, upload the file, then create the voice. The recording goes straight to storage and never passes through the API.

1. Ask for an upload URL, naming the recording’s type:

curl
curl https://api.lumavox.ai/v1/voices/upload-url \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"content_type": "audio/mpeg"}'
Response
{
"upload_id": "voi_a1B2c3D4e5F6g7H8",
"url": "https://…",
"content_type": "audio/mpeg",
"expires_at": "2026-10-24T09:45:00.000Z"
}

Accepted types: audio/wav, audio/x-wav, audio/wave, audio/vnd.wave, audio/mpeg, audio/mp3, audio/mp4, audio/x-m4a, audio/aac, audio/ogg, audio/opus, audio/webm, audio/flac and audio/x-flac. Anything else is a 422.

2. PUT the recording to url, with exactly the Content-Type you named and no Authorization header — the URL carries its own signature. It stops accepting the recording at expires_at, fifteen minutes after you asked. Its size is at most the platform’s limits.uploads.voice_bytes (Limits).

curl
curl -X PUT "<url>" \
-H "Content-Type: audio/mpeg" \
--data-binary @narrator.mp3

3. Create the voice from the upload:

curl
curl https://api.lumavox.ai/v1/voices \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"upload_id": "voi_a1B2c3D4e5F6g7H8",
"name": "Narrator",
"description": "Warm, measured, British.",
"consent": true
}'

consent: true is required. It confirms you have the right to clone this voice; without it the create is refused with a 422.

It answers 202 with the voice in status: pending. It cannot speak yet: poll GET /v1/voices/{voice_id} until it is ready, or failed with the reason in error. The clone happens after the create answers, so a recording the provider refuses shows up as failed, not as an error on this call.

A cloned voice can be spoken with every model: eleven_flash_v2_5, eleven_multilingual_v2 and eleven_v3.

Describe a voice in words and listen to candidates before you keep one.

1. Design:

curl
curl https://api.lumavox.ai/v1/voices/design \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Ship AI",
"prompt": "A calm, slightly synthetic female voice, mid-thirties, precise diction, like a starship computer."
}'

name is 1–120 characters and prompt 10–1,000.

Response (abridged)
{
"previews": [
{
"id": "<a preview id>",
"audio_base64": "SUQzBAAAAAAA…",
"media_type": "audio/mpeg",
"duration_seconds": 19.4,
"long_enough": true
}
],
"description": "A calm, precise female voice in her thirties…",
"script": "Systems nominal. Course plotted for the outer ring…",
"improved": true
}
  • previews are candidates, as base64 audio. How many come back can vary.
  • long_enough is false for a preview under 18 seconds — too short to judge a voice by. It can still be kept.
  • description is the brief the previews were designed from. Your prompt may have been rewritten into a fuller one; improved says whether it was.
  • script is what the previews say.

Nothing is saved by this call, and it is billed per design, whether or not you keep a candidate.

To listen to a preview:

curl
curl … | jq -r '.previews[0].audio_base64' | base64 --decode > preview.mp3

2. Keep one:

curl
curl https://api.lumavox.ai/v1/voices/design/save \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Ship AI",
"preview_id": "<a preview id>",
"prompt": "A calm, slightly synthetic female voice, mid-thirties, precise diction, like a starship computer."
}'

It answers 202 with the voice already ready — you can speak with it at once. Its spoken samples follow. Keeping is not billed; the design was. An optional description (up to 500 characters) is stored with it.

PATCH /v1/voices/{voice_id} changes only the fields you send:

Field Effect
name Rename it. 1–120 characters; cannot be null.
description Up to 500 characters. null clears it; it reads back as "".
models Which models it may be spoken with. Each must be one the provider offers, or it is a 422 at models.
provider_defaults The settings it speaks with by default: stability, similarity_boost, style, use_speaker_boost, speed — the same fields as provider_options.
curl
curl -X PATCH https://api.lumavox.ai/v1/voices/voi_a1B2c3D4e5F6g7H8 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Narrator (warm)", "provider_defaults": {"stability": 0.6}}'

There is no way to replace the recording. Clone a new voice and delete the old one.

POST /v1/voices/{voice_id}/sample:

  • on a failed voice, runs the clone again — the voice is pending until it finishes;
  • on a ready voice, regenerates its spoken samples;
  • on a pending voice, answers 409: it is already being processed.
curl
curl -X DELETE https://api.lumavox.ai/v1/voices/voi_a1B2c3D4e5F6g7H8 \
-H "Authorization: Bearer $API_KEY"

It answers 204, with no body. The voice, its samples and the voice made for it at the provider are removed. A speech request naming it afterwards is a 404.

  • Each organisation may keep a number of voices set by its plan. Over it, uploading, creating and keeping a design answer 409. Delete a voice, or ask for a higher limit. Your plan is at GET /v1/plan — see Credits and usage.
  • The platform’s own capacity for custom voices can fill up. Then new voices are refused with a 409 that says so, whatever your plan allows.
  • Uploading, creating, designing, keeping and retrying count against your rate limit, like a generation. Reading, editing and deleting do not.

The common envelope is described in Errors and limits. The ones specific to voices:

Status When
400 The body is not valid JSON.
402 The organisation is out of credits (on the calls that spend: uploading, creating, designing, keeping, retrying).
404 The voice is unknown, not your organisation’s, or a platform or built-in voice (for read, edit, delete and retry).
409 The plan’s voice limit is reached; the platform’s voice capacity is full; the upload never arrived, or was already used; or a retry of a voice that is still pending.
422 A content type that is not accepted; consent not true; a stored file that is empty, over the platform’s size for a recording, or not audio; a missing name; a models entry the provider does not offer; an unknown field. issues names the field.
429 Over your organisation’s rate limit.
502 The designer failed, or a preview could not be kept. The message says why.
503 Voice storage, the designer or the jobs that clone voices are not available on this deployment; or, on the list, your voices could not be looked up — the list never answers the built-in voice alone. Retry shortly.