Skip to content
Lumean API

Voices

Three catalogs with different schemas, different gender values, and different pagination bases. How to find a voice_id and what to do with it next.

Voices come from three independent sources and are not normalized to a common shape: different item schemas, different gender values, different list wrappers, and different page numbering bases. They share two things: the voices.read scope and the outcome — an ID that goes into a template. These three catalogs are read-only: a LumVoice voice cannot be created with an API key. Voice clones are created with a key in the reference, in the Cartesia and Fish Audio groups (voices.write scope).

Three catalogs and when to use which

SourceOperations
LumVoice — the platform's internal voicesGET /voices, /voices/library, /voices/public, /voices/tags, /voices/{voice}
ElevenLabs — the public Voice Library, provider objects as isGET /voices/elevenlabs/library
HeyGen — the provider's catalog with its own filter vocabularyGET /voices/heygen, /voices/heygen/filters

Within LumVoice, the lists serve different purposes. GET /voices returns your voices in any state, including those not fully cloned and those rejected by moderation: it is the worst list for picking a voice for an order. GET /voices/library is what you can voice with right now: your own ready voices plus those added from the catalog, with a nickname and an available flag. GET /voices/public is the shared catalog, filtered so that any voice taken from it is guaranteed to be usable.

Pagination: two incompatible bases

This is the most common cause of an “empty response” with a working key. The internal LumVoice lists put the familiar items + current_page / last_page / per_page / total into data and number pages from 1. The external catalogs return their own wrapper, voices + total / page / page_size / has_more, and number pages from 0: page=1 there is the second page. Full details are in the Pagination guide.

Gotcha

total means different things in the two external catalogs: in ElevenLabs it is the number of voices on the current page (the spec explicitly warns: “rely on has_more, not on total”), in HeyGen it is how many voices match the filters across all pages. You cannot build a common loop exit condition on total — use has_more.

gender values differ across all three

CatalogAllowed values
GET /voices/public (LumVoice)male, female, neutral
GET /voices/elevenlabs/librarymale, female
GET /voices/heygenmale, female, unknown

Only HeyGen has unknown: the provider returns some voices without a gender. You cannot build a common “gender” filter across the three catalogs. A related pitfall: in ElevenLabs, age is a strict enum — young, middle_aged, old — with an underscore; middle-aged returns 422.

How to make sure a LumVoice voice is usable for an order

The check depends on where the voice came from; the status alone is not enough.

SourceWhat to check
GET /voices/publicNothing: the catalog is filtered by voice_status and allow_usage_in_orders
GET /voices/libraryavailable; if false, the reason is in unavailable_reason
GET /voices (your own)languages[]: an order is possible in a language whose clone has completed

voice_status: "ready" means the voice is fully usable. partially_ready means some languages were cloned and some failed, while the working languages are available: what decides is not the overall status but the language's state in languages[] (status; the failure reason is in failure_reason).

Note

Sources disagree: llm.md describes partially_ready as a real state, but the enum of the voice_status parameter of GET /voices has five values — pending_clone, cloning, ready, clone_failed, archived. Read the value from responses, but do not filter by it.

publication_status and allow_usage_in_orders are about visibility in the catalog, not about eligibility for orders; the second flag does not affect your own voices. For users who had already added the voice to their library, the entry becomes available: false with an unavailable_reason (author_unpublished, admin_force_removed, author_deleted, owner_deleted).

HeyGen: the engine is a property of both the service and the voice

GET /templates/config-options?service=heygen lists the engines the service supports; which ones a specific voice supports is in the catalog item's voice_engines field. You need a value that is in both lists at once: of 2330 voices, 1947 support elevenLabs, and 383 support none of the listed ones. A mismatched “voice + engine” pair is rejected with 422 — and on the config.tts_settings.voice_id field, not on the engine field.

language, accent, and voice_engine values use the provider's spelling (English, en-US); GET /voices/heygen/filters returns the list with counts — call it first.

What to do with the ID you found

It goes into a template for the same service, and only there: there is no orders.voice_id field.

{
  "service_key": "elevenlabs",
  "name": "Narrator EN",
  "config": { "tts_settings": { "model_id": "eleven_multilingual_v2", "voice_id": "<VOICE_ID>" } }
}

For ElevenLabs and HeyGen, the value is in the item's voice_id, and the template's service_key is elevenlabs or heygen; for LumVoice, it is in the item's id. To swap the voice once without a new template, use the order's config_override; for building the config, see the Templates guide.

Gotcha

Three OpenAPI field descriptions give the path as config.voice_id — this short form is wrong. The working path is config.tts_settings.voice_id: that is how it appears in the description of the GET /voices operation itself, in the minimal valid config, and in all working examples.

Small things that trip you up silently

  • preview_urls live for about a minute. It is a “language code → signed URL or null” map, and you must not cache it for long; the external catalogs' equivalent is preview_url.
  • Tag names are localized. Filter by tag_ids[] and compare by code: name comes in the key's language.
  • A LumVoice ID is an opaque string. The schema calls it a UUID, but in examples it is a short key like k3nH9vRt2Lw7ZpXb: do not validate it as a UUID on the client.
  • ElevenLabs depends on an external API — it is the only catalog that returns 503.
  • Someone else's non-public voice returns 403; a nonexistent one returns 404.

Feed this page to an AI

Open .mdllm.md

Updated 09/04/2026 · spec 0.0.1