---
title: Voices
url: https://lumean.app/en/developers/docs/guide/voices
locale: en
description: "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."
updated: 2026-09-04
sources: [get-voices, get-voices-voice, get-voices-library, get-voices-public, get-voices-tags, get-voices-elevenlabs-library, get-voices-heygen, get-voices-heygen-filters]
errors: ["403", "404"]
spec_version: 0.0.1
---

# Voices

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

| Source | Operations |
| --- | --- |
| **LumVoice** — the platform's internal voices | `GET /voices`, `/voices/library`, `/voices/public`, `/voices/tags`, `/voices/{voice}` |
| **ElevenLabs** — the public Voice Library, provider objects as is | `GET /voices/elevenlabs/library` |
| **HeyGen** — the provider's catalog with its own filter vocabulary | `GET /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](/developers/docs/guide/pagination) guide.

> [!WARNING]
> `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

| Catalog | Allowed values |
| --- | --- |
| `GET /voices/public` (LumVoice) | `male`, `female`, `neutral` |
| `GET /voices/elevenlabs/library` | `male`, `female` |
| `GET /voices/heygen` | `male`, `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.

| Source | What to check |
| --- | --- |
| `GET /voices/public` | Nothing: the catalog is filtered by `voice_status` and `allow_usage_in_orders` |
| `GET /voices/library` | `available`; 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`).

### GET /voices/public

Public Voices Catalog

Справочник: https://lumean.app/en/developers/docs/api/get-voices-public

## 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.

```json
{
  "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](/developers/docs/guide/templates) guide.

> [!WARNING]
> 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`.**

### GET /voices/elevenlabs/library

ElevenLabs Voice Library

Справочник: https://lumean.app/en/developers/docs/api/get-voices-elevenlabs-library

---

- This page in the browser: https://lumean.app/en/developers/docs/guide/voices
- Full machine-readable reference: https://api.lumean.app/docs/llm.md
