<!-- Generated from openapi/v2.json (md/voice-clones.md) — do not edit. Run: npm run generate -->

[Interactive reference](https://api-docs.syllaby.io/) · [OpenAPI 3.1 spec](https://api-docs.syllaby.io/openapi/v2.json) · [Docs index (llms.txt)](https://api-docs.syllaby.io/llms.txt)

# Voice Clones

Clone a narration voice from audio samples, manage voice-clone slots, and list, update, or delete your clones.

## GET /voice-clones

**List voice clones** (operationId: `listVoiceClones`)

Lists the voice clones you own, newest first.

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": [
    {
      "id": 1,
      "name": "My Narrator",
      "gender": "female",
      "language": "en",
      "description": "Warm, conversational narrator voice.",
      "status": "completed",
      "slot_type": "default",
      "preview_url": "https://cdn.syllaby.dev/voices/clone-1/preview.mp3",
      "created_at": "2026-01-01T12:00:00.000000Z"
    },
    {
      "id": 2,
      "name": "Podcast Host",
      "gender": "female",
      "language": "en",
      "description": "Warm, conversational narrator voice.",
      "status": "pending",
      "slot_type": "addon",
      "preview_url": null,
      "created_at": "2026-01-01T12:00:00.000000Z"
    }
  ]
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active **paid** subscription. The v2 public API is paid-only — every faceless and preset endpoint (reads included) requires an active paid subscription, and trials are not eligible. Two codes distinguish the cause on this `403`: `PAID-SUBSCRIPTION-REQUIRED` (subscribed but on a trial — access unlocks automatically once the subscription becomes paid) and `SUBSCRIPTION-REQUIRED` (never subscribed, expired, or canceled). Rejected before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without a paid subscription.
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## POST /voice-clones

**Create a voice clone** (operationId: `createVoiceClone`)

Clones a voice from 1–3 audio samples. Send as `multipart/form-data`.

Returns `201` with the clone in a **`pending`** state — cloning runs asynchronously. Poll `GET /voice-clones/{id}` and read `data.status` (`pending` → `reviewing` → `completed`, or `failed`); the sample audio preview appears at `data.preview_url` once `completed`.

**Slots.** Each plan includes a fixed number of voice-clone slots. When your included slots are full you can buy an extra one by sending `purchase_addon: true` — this **💳 charges credits** (the price is `cost` on `GET /voice-clones/slots`) and the response `meta.credits` reports the charge and your remaining balance. Without a free slot and without `purchase_addon`, the request returns `403`; with `purchase_addon` but too few credits, it returns `402`.

### Request body

Required.

- `terms` (string, required) — Required — you must accept the voice-cloning terms. Send a truthy value (`true`, `1`, `"yes"`, `"on"`); consent is a legal boundary and the request is rejected without it.
- `name` (string, required) — Required — display name for the voice clone (max 20 characters).
- `description` (string|null) — Optional — free-text description of the voice (max 200 characters).
- `gender` (string, required) — Required — voice gender: `male`, `female`, or `neutral`. Allowed values: `male`, `female`, `neutral`.
- `language` (string|null) — Optional — ISO language code for the voice (max 3 characters, e.g. `en`).
- `purchase_addon` (boolean|null) — Optional — set `true` to buy an extra voice-clone slot when your included slots are full. Sent over `multipart/form-data`, so the accepted truthy spellings are `true`, `1`, `on` and `yes` (case-insensitive); `false`, `0`, `off`, `no` and an empty value all decline. Any other value returns `422` rather than silently declining the purchase. Charges credits (see `GET /voice-clones/slots` for the price); a `false`/omitted value with no free slot returns `403`. If purchasing and your balance is too low, the request returns `402`.
- `samples` (array of string, required) — Required — 1 to 3 audio sample files of the voice to clone. Each file may be mp3, wav, m4a, or webm, up to 10 MB. Send as `multipart/form-data` (`samples[]`).

Example request:

```json
{
  "terms": true,
  "name": "My Narrator",
  "gender": "female",
  "language": "en",
  "description": "Warm, conversational narrator voice.",
  "samples": [
    "<binary audio file>"
  ],
  "purchase_addon": true
}
```

### Response `201`

```json
{
  "message": "Success.",
  "status": 201,
  "data": {
    "id": 1,
    "name": "My Narrator",
    "gender": "female",
    "language": "en",
    "description": "Warm, conversational narrator voice.",
    "status": "pending",
    "slot_type": "addon",
    "preview_url": null,
    "created_at": "2026-01-01T12:00:00.000000Z"
  },
  "meta": {
    "credits": {
      "cost": 15,
      "remaining": 885
    }
  }
}
```

### Errors

- `401` — Unauthenticated
- `402` — Insufficient credits. The request is authenticated and valid, but the account balance is too low — top up and retry.
- `403` — Forbidden — two distinct causes share this status: 1. **No active subscription** (code `SUBSCRIPTION-REQUIRED`, message "An active subscription is required.") — checked first by the active-subscription gate, before any ownership or precondition logic. The v2 public API has no free tier. 2. **Authorization / ownership or precondition failure** — the subscription is active but the action is not allowed: the resource belongs to another account, or a render precondition is unmet (e.g. `A script is required before rendering.`, `Voice was not provided.`, the video is busy, or storage is full).
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `413` — The request body exceeded the server upload limit. This is checked before validation, so the response names no specific field. Send fewer or smaller files — a single file over the documented per-field limit returns `422` on that key instead.
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## GET /voice-clones/slots

**Get voice-clone slot availability** (operationId: `getVoiceCloneSlots`)

Reports your voice-clone slot capacity and whether you can buy another one. Use this before `POST /voice-clones` to decide whether to send `purchase_addon`.

- `default_max` — slots included with your plan.
- `addon_cap` — the maximum number of extra slots you may purchase.
- `current_addons` — extra slots you have already bought.
- `lifetime_purchases` — total add-on slots ever purchased.
- `filled` / `available` — slots in use and slots free right now.
- `can_buy` — whether buying another slot is currently possible (add-on cap not reached, enough credits, not on a trial).
- `cost` — credits charged to buy one extra slot.

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "default_max": 1,
    "addon_cap": 5,
    "current_addons": 2,
    "lifetime_purchases": 2,
    "filled": 2,
    "available": 1,
    "can_buy": true,
    "cost": 15
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active **paid** subscription. The v2 public API is paid-only — every faceless and preset endpoint (reads included) requires an active paid subscription, and trials are not eligible. Two codes distinguish the cause on this `403`: `PAID-SUBSCRIPTION-REQUIRED` (subscribed but on a trial — access unlocks automatically once the subscription becomes paid) and `SUBSCRIPTION-REQUIRED` (never subscribed, expired, or canceled). Rejected before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without a paid subscription.
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## GET /voice-clones/{id}

**Get a voice clone** (operationId: `getVoiceClone`)

Returns a single voice clone you own — poll this to track cloning status (`pending` → `reviewing` → `completed`/`failed`). Responds with `404` when the id does not exist or belongs to another account.

### Parameters

- `id` (path, integer, required) — Identifier of the faceless video. Example: `1`

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "id": 1,
    "name": "My Narrator",
    "gender": "female",
    "language": "en",
    "description": "Warm, conversational narrator voice.",
    "status": "completed",
    "slot_type": "default",
    "preview_url": "https://cdn.syllaby.dev/voices/clone-1/preview.mp3",
    "created_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active **paid** subscription. The v2 public API is paid-only — every faceless and preset endpoint (reads included) requires an active paid subscription, and trials are not eligible. Two codes distinguish the cause on this `403`: `PAID-SUBSCRIPTION-REQUIRED` (subscribed but on a trial — access unlocks automatically once the subscription becomes paid) and `SUBSCRIPTION-REQUIRED` (never subscribed, expired, or canceled). Rejected before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without a paid subscription.
- `404` — Not found
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## PATCH /voice-clones/{id}

**Update a voice clone** (operationId: `updateVoiceClone`)

Updates a voice clone's `name`, `gender`, and `description`. Only the fields you send are changed. Owner-scoped — a clone belonging to another account responds with `404`.

### Parameters

- `id` (path, integer, required) — Identifier of the faceless video. Example: `1`

### Request body

Required.

- `name` (string, required) — Required — display name for the voice clone (max 125 characters).
- `description` (string|null) — Optional — free-text description of the voice (max 255 characters).
- `gender` (string, required) — Required — voice gender: `male`, `female`, or `neutral`. Allowed values: `male`, `female`, `neutral`.

Example request:

```json
{
  "name": "My Narrator",
  "gender": "female",
  "description": "Warm, conversational narrator voice."
}
```

### Response `200`

```json
{
  "message": "Success.",
  "status": 200,
  "data": {
    "id": 1,
    "name": "My Narrator",
    "gender": "female",
    "language": "en",
    "description": "Warm, conversational narrator voice.",
    "status": "completed",
    "slot_type": "default",
    "preview_url": "https://cdn.syllaby.dev/voices/clone-1/preview.mp3",
    "created_at": "2026-01-01T12:00:00.000000Z"
  }
}
```

### Errors

- `401` — Unauthenticated
- `403` — No active **paid** subscription. The v2 public API is paid-only — every faceless and preset endpoint (reads included) requires an active paid subscription, and trials are not eligible. Two codes distinguish the cause on this `403`: `PAID-SUBSCRIPTION-REQUIRED` (subscribed but on a trial — access unlocks automatically once the subscription becomes paid) and `SUBSCRIPTION-REQUIRED` (never subscribed, expired, or canceled). Rejected before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without a paid subscription.
- `404` — Not found
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `422` — Validation error
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).

## DELETE /voice-clones/{id}

**Delete a voice clone** (operationId: `deleteVoiceClone`)

Deletes a voice clone you own and removes the underlying provider voice. Returns `204 No Content`. Owner-scoped — a clone belonging to another account responds with `404`.

### Parameters

- `id` (path, integer, required) — Identifier of the faceless video. Example: `1`

### Response `204`

Empty body (`204 No Content`).

### Errors

- `401` — Unauthenticated
- `403` — No active **paid** subscription. The v2 public API is paid-only — every faceless and preset endpoint (reads included) requires an active paid subscription, and trials are not eligible. Two codes distinguish the cause on this `403`: `PAID-SUBSCRIPTION-REQUIRED` (subscribed but on a trial — access unlocks automatically once the subscription becomes paid) and `SUBSCRIPTION-REQUIRED` (never subscribed, expired, or canceled). Rejected before any ownership, credit, or validation check. Only `GET /me`, `GET /credits/costs`, and `GET /credits/history` are reachable without a paid subscription.
- `404` — Not found
- `405` — The URL exists but not for this HTTP method — e.g. `GET /presets/faceless/{preset}`, which is only defined for `PATCH` and `DELETE`. Check the verb against the endpoint reference; the `Allow` header lists the methods this path accepts.
- `409` — Email address not verified. The token is valid and the account exists, but the user has not confirmed their email address. This gate fronts every v2 endpoint. Have the user complete verification, then retry.
- `429` — Rate limit exceeded — requests are limited per API token (30 per minute by default). The response carries `Retry-After` (seconds to wait) and `X-RateLimit-Reset` (Unix timestamp when the window resets); back off until then and retry. Successful responses include `X-RateLimit-Limit` and `X-RateLimit-Remaining` so you can pace requests proactively.

Error shapes and examples are shared across endpoints — see the **Error responses** section in [getting-started](getting-started.md).
