# Voices

Every voice you can speak with, in one collection: the ready-made Neurall catalog, plus the voices you clone yourself. Cloning posts here rather than to generations, because what comes back is a reusable id and not a file.

Base URL: https://api.neurall.io. Authenticate with `Authorization: nl-YOUR_API_KEY`.

### List Voices

`GET /v3/voices`

Every voice this project can speak with: its own cloned voices first (`owner: project`), then the Neurall catalog (`owner: neurall`), which is ready-made and identical for every caller. Filter with `?owner=`.

Pass a voice's `id` (or a catalog `slug`, the stable handle worth hardcoding) as `args.voiceId` in an audio/voiceover or video/avatar generation. `preview` is a presigned audition clip on catalog voices and expires after ~6 hours; a clone carries a `status` instead, until it is `done`.

```bash
curl -X GET "https://api.neurall.io/v3/voices" \
  -H "Authorization: nl-YOUR_API_KEY"
```

**Query**

- `owner` ('neurall' | 'project'): `project` for this project's own clones, `neurall` for the ready-made catalog. Omit for both. Anything else is a 400. `tag` and `locale` describe catalog rows only, so they are ignored on clones rather than silently hiding them.
- `tag` (string[]): Only voices carrying ALL of these tags. May repeat (?tag=female&tag=warm). Tags are lowercase descriptors: gender, age, accent, style, plus the category (narration, conversational, character, creature).
- `locale` (string): Only voices recorded for one locale, matched exactly against the voice's own `locale` (e.g. `en-US`). It is not a prefix: `en` returns nothing.

**Responses**

- `200`: This project's clones, then the published catalog
  - `voices` (object[]): A voice this project can speak with. `owner: neurall` is a catalog voice, ready to use and identical for every caller. `owner: project` is one of the project's own clones, which carries the same status lifecycle as a Generation while it trains. A voice's own id is what goes in `args.voiceId`.

  Example:

  ```json
  {
    "voices": [
      {
        "id": "d77c4b2e-5a90-4f13-b8c6-1e2f3a4b5c6d",
        "owner": "project",
        "status": "done",
        "name": "Founder voice"
      },
      {
        "id": "5b3a9d10-2c47-4e8f-a6b1-9d0c3e7f2a84",
        "owner": "neurall",
        "slug": "aria",
        "name": "Aria",
        "tags": [
          "female",
          "warm",
          "narration"
        ],
        "locale": "en-US",
        "favorite": false,
        "preview": "https://cdn.neurall.io/…/aria-preview.mp3"
      }
    ]
  }
  ```
- `401`: Unauthorized

### Clone a Voice

`POST /v3/voices`

Clone a voice from an audio sample: upload about thirty seconds of clean speech to /v3/files/upload, then POST its file id here. Returns immediately with `status: running`; poll GET /v3/voices/{id} until it is `done`, or pass `webhookUrl` and be told.

The id you get back IS the voiceId, usable in `args.voiceId` on audio/voiceover and video/avatar. Charged on submit like a generation, refunded on error or timeout. An organization may have 5 voices training at once.

```bash
curl -X POST "https://api.neurall.io/v3/voices" \
  -H "Authorization: nl-YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "<name>",
    "audio": "<audio>",
    "description": "<description>",
    "webhookUrl": "<webhookUrl>"
  }'
```

**Body**

- `name` (string, required): Name for the voice.
- `audio` (string, required): File id of the audio sample, from POST /v3/files/upload. MP3, WAV, M4A, OGG, FLAC or WebM, holding a real audio stream.
- `description` (string)
- `webhookUrl` (string): Optional https URL to POST the finished voice to (`voice.done` / `voice.error`), signed exactly like a generation webhook.

**Responses**

- `200`: The voice, still training
  - `voice` (object): A voice this project can speak with. `owner: neurall` is a catalog voice, ready to use and identical for every caller. `owner: project` is one of the project's own clones, which carries the same status lifecycle as a Generation while it trains. A voice's own id is what goes in `args.voiceId`.

  Example:

  ```json
  {
    "voice": {
      "id": "d77c4b2e-5a90-4f13-b8c6-1e2f3a4b5c6d",
      "owner": "project",
      "status": "running",
      "name": "Founder voice",
      "requestDate": "2026-08-25T17:04:12.000Z"
    }
  }
  ```
- `400`: Missing name or audio, or the sample is too short
- `401`: Unauthorized
- `402`: Insufficient balance
- `429`: Too many voices training at once (Retry-After header)

### Get Voice

`GET /v3/voices/{id}`

Resolves ONE voice without pulling the whole list: a catalog voice by uuid or by slug, or one of this project's own clones by its id. The shapes cannot collide, so a caller holding a voiceId never has to know which kind it is holding.

On a clone this is the poll: after POST /v3/voices it answers `status: running` until the voice is `done` (or `error`), which is the same loop a generation uses.

On a catalog voice there are two reasons to reach for it. First, `preview` is a presigned audition clip that expires after about 6 hours, so this is how you refresh the sample for a voice your app already knows about and lets a user play. Second, it is the cheap way to confirm a slug you hardcoded is still published: a retired voice answers 404 here rather than failing later, inside a generation you have already paid for.

```bash
curl -X GET "https://api.neurall.io/v3/voices/<id>" \
  -H "Authorization: nl-YOUR_API_KEY"
```

**Path**

- `id` (string, required): A catalog voice's uuid or slug, or one of this project's clone ids.

**Responses**

- `200`: The voice
  - `voice` (object): A voice this project can speak with. `owner: neurall` is a catalog voice, ready to use and identical for every caller. `owner: project` is one of the project's own clones, which carries the same status lifecycle as a Generation while it trains. A voice's own id is what goes in `args.voiceId`.

  Example:

  ```json
  {
    "voice": {
      "id": "d77c4b2e-5a90-4f13-b8c6-1e2f3a4b5c6d",
      "owner": "project",
      "status": "done",
      "name": "Founder voice",
      "requestDate": "2026-08-25T17:04:12.000Z",
      "responseDate": "2026-08-25T17:09:35.000Z"
    }
  }
  ```
- `401`: Unauthorized
- `404`: Voice not found

### Delete a Voice

`DELETE /v3/voices/{id}`

Delete one of this project's cloned voices. It stops resolving everywhere, so anything still passing that id as `args.voiceId` starts failing, and there is no undelete: cloning again is a new generation. Catalog voices are not deletable.

```bash
curl -X DELETE "https://api.neurall.io/v3/voices/<id>" \
  -H "Authorization: nl-YOUR_API_KEY"
```

**Path**

- `id` (string, required)

**Responses**

- `200`: Deleted
  - `success` (boolean)

  Example:

  ```json
  {
    "success": true
  }
  ```
- `401`: Unauthorized
- `404`: Not one of this project's cloned voices
