---
title: "POST /voices/cartesia/clones — Create Cartesia Voice Clone"
url: https://lumean.app/es/developers/docs/api/post-voices-cartesia-clones
locale: es
description: "Create Cartesia Voice Clone"
generated: 2026-10-17
operationId: cartesiaVoice.storeClone
method: POST
path: /voices/cartesia/clones
scopes: [voices.write]
spec_version: 0.0.1
---

# POST /voices/cartesia/clones

**Create Cartesia Voice Clone**

Clones a voice from a sample recording. The sample goes base64-encoded (a data URI
is fine too) into the `reference_audio` field of the JSON body, up to 20 MB;
`consent_biometric: true` (consent to biometric data processing) and `voice_owner`
(`self` or `third_party`) are recorded in the consent journal; the former
`consent_confirmed: true` is still accepted instead until consents become mandatory.

Cloning is asynchronous. The `201` response comes back at once with the clone in
`pending` (or straight away in `failed` with `error.reason: dispatch_failed` if the
queue is unavailable — then retry via
`POST /voices/cartesia/clones/{voiceClone}/retry`). After that it goes `pending` →
`cloning` → `ready` (or `failed`). Progress arrives as the `voice_clone.updated`
WebSocket event on `private-cartesia-voice-clones.{userId}`; without WebSocket, poll
`GET /voices/cartesia/clones`.

Checks, in order: access to the service (`404 service_not_found`), subscription
(`402 no_subscription`) — a live subscription that includes Cartesia is required only
if the service is closed to paying from the balance; otherwise cloning works without
one — and the clone cap (`422 voice_clone_quota_exceeded`, the `max` field holds the
cap): 2 clones without a subscription (the fallback cap), the plan's cap with one. An
exhausted token quota does not block cloning.

**Required key permission:** `voices.write`.

- Permiso requerido: `voices.write`

## Petición

```bash
curl -X POST 'https://api.lumean.app/api/public/voices/cartesia/clones' \
  -H 'Accept: application/json' \
  -H 'X-API-KEY: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "display_name": "My voice",
  "reference_audio": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA=",
  "language": "ru",
  "description": "Audiobook narrator",
  "consent_biometric": true,
  "voice_owner": "self"
}'
```

## Parámetros

Sin parámetros.

## Cuerpo de la petición

A new Cartesia voice clone: a base64 sample recording plus consent to use the voice.
Accepted as `application/json`.

Content-Type: `application/json`

| Campo | Tipo | Oblig. | Descripción |
| --- | --- | --- | --- |
| `display_name` | `string` — ≤ 128 caracteres | obligatorio | Clone name — visible only to you, up to 128 characters. |
| `reference_audio` | `string` | obligatorio | The voice sample in base64; a data URI (`data:audio/wav;base64,…`) works too. Up to 20 MB once decoded; any common audio format (WAV, MP3 and the like). It should be clean speech by a single person, without music or noise. |
| `consent_confirmed` | `string` | obligatorio | The former checkbox "the voice is yours or you hold the right to use it". Needed only if `consent_biometric: true` is not sent; once consents become mandatory, send `consent_biometric` and `voice_owner` instead. |
| `language` | `string` — `en` · `ru` · `uk` · `de` · `fr` · `es` · `pt` · `it` · `nl` · `pl` · `sv` · `tr` · `cs` · `sk` · `bg` · `ro` · `hu` · `el` · `fi` · `da` · `no` · `hr` · `ka` · `he` · `ar` · `hi` · `bn` · `ta` · `te` · `gu` · `kn` · `ml` · `mr` · `pa` · `ja` · `ko` · `zh` · `vi` · `th` · `id` · `ms` · `tl` — nullable | — | Language of the sample, an ISO 639-1 code from the list. Omitted — the default language of the worker. |
| `description` | `string` — ≤ 500 caracteres — nullable | — | Your description of the clone, up to 500 characters. |
| `consent_biometric` | `boolean` | — | Consent to processing of biometric data — the voice sample (https://lumean.app/consent/biometric). Recorded in the consent journal together with the clone id. `true` — consent given. |
| `voice_owner` | `string` — `self` · `third_party` — nullable | — | Whose voice is in the sample: `self` — yours, `third_party` — someone else's, and you hold their consent. |

**Ejemplo**

```json
{
  "display_name": "My voice",
  "reference_audio": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA=",
  "language": "ru",
  "description": "Audiobook narrator",
  "consent_biometric": true,
  "voice_owner": "self"
}
```

## Escenarios del cuerpo

### Sample as plain base64

reference_audio is the file content in base64 (no line breaks), up to 20 MB once decoded; the format is detected from the content (WAV, MP3, M4A/MP4, AAC, OGG, WebM, FLAC). consent_biometric must be exactly true, voice_owner is self or third_party. The reference_audio here is an empty WAV for brevity: a real sample is 10–60 seconds of clean speech by one person.

```json
{
  "display_name": "My voice",
  "reference_audio": "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA=",
  "language": "ru",
  "description": "Audiobook narrator",
  "consent_biometric": true,
  "voice_owner": "self"
}
```

### Sample as a data URI, minimal fields

A data:audio/...;base64, prefix is accepted and stripped by the server. language and description may be omitted.

```json
{
  "display_name": "Podcast voice",
  "reference_audio": "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQAAAAA=",
  "consent_biometric": true,
  "voice_owner": "self"
}
```

## Respuestas

### 201 — Created. `data` holds the new object in full.

**Campos de la respuesta**

| Campo | Tipo | Oblig. | Descripción |
| --- | --- | --- | --- |
| `success` | `boolean` | obligatorio | Success flag. Branch on the HTTP status code instead: any 2xx response carries `true` here, while an error comes back in a different shape that need not contain this field at all. |
| `message` | `string` | obligatorio | A human-readable note in the account language. Fine to show to a user; do not parse it — the wording changes without notice and is not an error code. |
| `data` | `CartesiaVoiceClone` | obligatorio | The new clone — in `pending` (or straight away in `failed` with `error.reason: dispatch_failed` if the queue is unavailable). |
| `data.id` | `string` | obligatorio | Clone UUID. Use it in `/voices/cartesia/clones/{voiceClone}/…` paths; it does NOT go into a template — `voice_id` does. |
| `data.provider` | `string` — `cartesia` | obligatorio | Clone provider — always `cartesia`. |
| `data.display_name` | `string` | obligatorio | Clone name given at creation. |
| `data.language` | `string` | obligatorio | Language of the sample (ISO 639-1), or `null` if none was sent. |
| `data.description` | `string` | obligatorio | Your description of the clone, or `null`. |
| `data.status` | `string` | obligatorio | Status: `pending` → `cloning` → `ready` or `failed`; `archived` — retired by the owner; `banned` — blocked by a moderator. Only `ready` can be used for narration. |
| `data.voice_id` | `string` | obligatorio | The Cartesia voice id — the value for `tts_settings.voice_id` of a template with `service_key: cartesia`. Present only for `ready`, otherwise `null`. |
| `data.attempts` | `integer` | obligatorio | Cloning attempt number: 1 on creation, +1 for every retry. |
| `data.error` | `object` — nullable | obligatorio | Why the last attempt failed; `null` while there has been no failure. |
| `data.error.reason` | `string` | obligatorio | Machine-readable failure code: `processing_error`, `max_retries`, `network`, `timeout`, `dispatch_failed`. Retry with `POST /voices/cartesia/clones/{voiceClone}/retry`; if `processing_error` keeps coming back, use a different sample. |
| `data.error.message` | `string` | obligatorio | Human-readable explanation of the failure, or `null`. Branch on `reason`, not on this text. |
| `data.created_at` | `string` | obligatorio | When the clone was created. |
| `data.completed_at` | `string` | obligatorio | When the clone became `ready`; `null` until then. |
| `data.failed_at` | `string` | obligatorio | When the last attempt failed; `null` if it has not. |
| `data.banned_at` | `string` | obligatorio | When a moderator banned the clone (`status = banned`); `null` if it is not banned. |
| `data.ban_reason` | `string` | obligatorio | Reason for the ban given by the moderator, or `null`. A banned clone can be neither used nor archived. |

**Ejemplo**

```json
{
  "success": true,
  "message": "Success",
  "data": {
    "id": "01a10e08-27fe-716f-b428-6bc739602ca6",
    "provider": "cartesia",
    "display_name": "My voice",
    "language": "ru",
    "description": null,
    "status": "pending",
    "voice_id": null,
    "attempts": 1,
    "error": null,
    "created_at": "2026-10-06T09:15:00+00:00",
    "completed_at": null,
    "failed_at": null,
    "banned_at": null,
    "ban_reason": null
  }
}
```

### 401 — The key is missing, unknown, disabled or expired.

**Campos de la respuesta**

| Campo | Tipo | Oblig. | Descripción |
| --- | --- | --- | --- |
| `success` | `boolean` | obligatorio | Always `false` in error responses. |
| `message` | `string` | obligatorio | Error text in the key's language (set through the `X-Locale` header). |
| `data` | `null` — nullable | obligatorio | Always `null` when the request is rejected before processing. |

**Ejemplo**

```json
{
  "success": false,
  "message": "API key is missing. Provide it in the X-API-KEY header.",
  "data": null
}
```

### 402 — No live subscription that includes Cartesia (`reason: no_subscription`). Returned only if the service is closed to paying from the balance.

**Campos de la respuesta**

| Campo | Tipo | Oblig. | Descripción |
| --- | --- | --- | --- |
| `success` | `boolean` | obligatorio | Always `false` in error responses. |
| `message` | `string` | obligatorio | The reason for the rejection in words, in the account language. Meant for display; do not branch on it, the wording changes. |
| `reason` | `string` | obligatorio | Machine-readable reason of a business refusal (for example, `input_expired`, `insufficient_balance`). Branch on it, not on the `message` text. |
| `error_code` | `string` | obligatorio | The same machine-readable code as in `reason` — a second name for the field. The values always match. |

**Ejemplo**

```json
{
  "success": false,
  "message": "An active subscription is required.",
  "reason": "no_subscription",
  "error_code": "no_subscription"
}
```

### 403 — The key lacks the `voices.write` permission.

**Campos de la respuesta**

| Campo | Tipo | Oblig. | Descripción |
| --- | --- | --- | --- |
| `success` | `boolean` | obligatorio | Always `false` in error responses. |
| `message` | `string` | obligatorio | Error text in the key's language (set through the `X-Locale` header). |

**Ejemplo**

```json
{
  "success": false,
  "message": "This API key does not have permission to perform this action."
}
```

### 404 — The Cartesia service is closed to the key owner (`reason: service_not_found`).

**Campos de la respuesta**

| Campo | Tipo | Oblig. | Descripción |
| --- | --- | --- | --- |
| `success` | `boolean` | obligatorio | Always `false` in error responses. |
| `message` | `string` | obligatorio | The reason for the rejection in words, in the account language. Meant for display; do not branch on it, the wording changes. |
| `reason` | `string` | obligatorio | Machine-readable reason of a business refusal (for example, `input_expired`, `insufficient_balance`). Branch on it, not on the `message` text. |
| `error_code` | `string` | obligatorio | The same machine-readable code as in `reason` — a second name for the field. The values always match. |

**Ejemplo**

```json
{
  "success": false,
  "message": "Service not found.",
  "reason": "service_not_found",
  "error_code": "service_not_found"
}
```

### 422 — Two different bodies share this code. Validation error: `{success: false, message, errors: {field: [text]}}` with no `reason` (a required field is missing, no `consent_biometric`/`consent_confirmed` consent, `language` is not one of the 42 codes, the sample is not valid base64, is larger than 20 MB or is not WAV/MP3/M4A/AAC/OGG/WebM/FLAC). Clone cap of the plan: `{success: false, message, reason: "voice_clone_quota_exceeded", error_code, max}` — `max` is the cap; archive a clone (`DELETE`) or move to a higher plan. Tell them apart by the presence of `reason`.

**Campos de la respuesta**

| Campo | Tipo | Oblig. | Descripción |
| --- | --- | --- | --- |
| `message` | `string` | obligatorio | Errors overview. |
| `errors` | `object` | obligatorio | A detailed description of each field that failed validation. |
| `errors.*` | `array<string>` — claves arbitrarias | — |  |

**Ejemplo**

```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "consent_confirmed": [
      "The consent confirmed field must be accepted."
    ],
    "reference_audio": [
      "The file is too large: 20 MB at most."
    ]
  }
}
```

### 429 — The clone rate limit is exceeded (`reason: rate_limit_exceeded`): by default 6 attempts per minute and 60 per day per user, shared with the web cabinet and LumVoice cloning. Every attempt counts, including one rejected by validation. Wait `retry_after` seconds (the `Retry-After` header).

**Campos de la respuesta**

| Campo | Tipo | Oblig. | Descripción |
| --- | --- | --- | --- |
| `success` | `boolean` | obligatorio | Always `false` in error responses. |
| `message` | `string` | obligatorio | Error text in the key's language (set through the `X-Locale` header). |
| `reason` | `string` | obligatorio | Machine-readable failure reason — branch on this, not on the message text. |
| `error_code` | `string` | obligatorio | The same machine-readable code as in `reason` — a second name for the field. The values always match; branch on either. |
| `retry_after` | `integer` — nullable | — | How many seconds to wait before retrying (the same value is in the `Retry-After` header). |

**Ejemplo**

```json
{
  "success": false,
  "message": "Too many attempts. Please wait a moment and try again.",
  "reason": "rate_limit_exceeded",
  "error_code": "rate_limit_exceeded",
  "retry_after": 30
}
```

---

- Esta página en el navegador: https://lumean.app/es/developers/docs/api/post-voices-cartesia-clones
- Referencia completa legible por máquina: https://api.lumean.app/docs/llm.md
