# Voices

A text-to-speech model on Layer speaks in the voice of a **reference set**. There is no `voice_id` field on the inference request: you pick a voice by passing a voice reference set in `reference_sets`, and a model that needs one refuses a request without it.

This applies to every ElevenLabs voice model:

| Base model                 | base\_model\_id                |
| -------------------------- | ------------------------------ |
| ElevenLabs TTS V4          | ELEVEN\_LABS\_TTS\_V4          |
| ElevenLabs TTS V4 Turbo    | ELEVEN\_LABS\_TTS\_V4\_TURBO   |
| ElevenLabs TTS V3          | ELEVEN\_LABS\_TTS\_V3          |
| ElevenLabs Multilingual V2 | ELEVEN\_LABS\_MULTILINGUAL\_V2 |
| ElevenLabs Voice Changer   | ELEVEN\_LABS\_VOICE\_CHANGER   |

One voice reference set works on all five: the same voice is bound to each of them.

## Use a ready-made voice

Layer ships a library of character voices (Amelia, David, Hope and more) as reference sets that are already bound to the models above. List them like any other reference set, optionally searching by name:

```bash
curl "https://api.app.layer.ai/api/v2/workspaces/$WORKSPACE_ID/reference-sets?search=David" \
  -H "Authorization: Bearer $LAYER_TOKEN"
# → { "reference_sets": [{ "reference_set_id": "…", "name": "David", "category": "character", "has_active_finetune": true }], … }
```

Then pass its `reference_set_id` as `set_id` when you generate:

```bash
curl -X POST https://api.app.layer.ai/api/v2/workspaces/$WORKSPACE_ID/inferences \
  -H "Authorization: Bearer $LAYER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "base_model_id": "ELEVEN_LABS_TTS_V4",
    "prompt": "[whispering] Keep your voice down. The patrol passes at midnight.",
    "reference_sets": [{ "set_id": "REFERENCE_SET_ID" }]
  }'
```

The run is [asynchronous](/docs/async-jobs): poll `GET …/inferences/{inference_id}` until it completes.

## Make your own voice

Your own voice is a reference set you train with one of three **training frameworks**:

| training\_framework | Starts from                             | Inputs                                                                           |
| ------------------- | --------------------------------------- | -------------------------------------------------------------------------------- |
| voice-clone         | Recordings of the voice                 | 1 to 10 audio clips in the reference set.                                        |
| voice-design        | A written description of the voice      | training\_parameters.training\_prompt, plus at most one optional reference clip. |
| voice-import        | An ElevenLabs voice ID you already have | training\_parameters.voice\_id; no files.                                        |

Train on `ELEVEN_LABS_TTS_V4`, `ELEVEN_LABS_TTS_V4_TURBO` or `ELEVEN_LABS_TTS_V3`. Whichever you pick, the finished voice is bound to all five ElevenLabs voice models, so you can generate with any of them afterwards.

Every framework follows the same flow: create a reference set, start a training run on it, poll the run, then generate with the set.

1. **Create a reference set**  
For `voice-clone`, [upload](/docs/files) your clips first (`audio/wav` or `audio/mpeg`) and pass their IDs. For `voice-import`, and for `voice-design` without a reference clip, `file_ids` can be empty.  
```bash  
curl -X POST https://api.app.layer.ai/api/v2/workspaces/$WORKSPACE_ID/reference-sets \
  -H "Authorization: Bearer $LAYER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{  
    "name": "Captain Mara",  
    "category": "character",  
    "file_ids": ["AUDIO_FILE_ID_1", "AUDIO_FILE_ID_2"]  
  }'  
# → { "reference_set_id": "…", "name": "Captain Mara", "asset_count": 2, … }  
```
2. **Start a training run**  
Pass the framework and its parameters. See the examples for each framework below.  
```bash  
curl -X POST https://api.app.layer.ai/api/v2/workspaces/$WORKSPACE_ID/training-runs \
  -H "Authorization: Bearer $LAYER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{  
    "reference_set_id": "REFERENCE_SET_ID",  
    "base_model_id": "ELEVEN_LABS_TTS_V4",  
    "training_framework": "voice-clone"  
  }'  
# → { "training_run_id": "…", "state": "in_progress", "poll_interval_seconds": … }  
```  
To check the price first, send the same body to `POST …/training-runs/estimate`.
3. **Poll the run**  
```bash  
curl https://api.app.layer.ai/api/v2/workspaces/$WORKSPACE_ID/training-runs/TRAINING_RUN_ID \
  -H "Authorization: Bearer $LAYER_TOKEN"  
```  
Poll every `poll_interval_seconds` until `state` is `terminal_success` or `terminal_failure`. On failure, read `error` and `error_code`. You can also pass a `webhook` when you start the run instead of polling (see [Webhooks](/docs/webhooks)).
4. **Generate with the voice**  
Pass the reference set exactly as in [Use a ready-made voice](#use-a-ready-made-voice), with any of the five base models.

### Clone a voice

`voice-clone` needs between 1 and 10 audio clips in the reference set. Clean speech with no music or background noise gives the closest match; a few minutes in total is plenty.

```json
{
  "reference_set_id": "REFERENCE_SET_ID",
  "base_model_id": "ELEVEN_LABS_TTS_V4",
  "training_framework": "voice-clone"
}
```

### Design a voice

`voice-design` creates a new voice from a description in `training_parameters.training_prompt`. On ElevenLabs the description runs 20 to 1000 characters, and three optional knobs shape the result:

| Parameter        | Range    | Effect                                                                              |
| ---------------- | -------- | ----------------------------------------------------------------------------------- |
| loudness         | \-1 to 1 | How loud the designed voice speaks.                                                 |
| guidance\_scale  | 0 to 100 | How closely the voice follows the description.                                      |
| prompt\_strength | 0 to 1   | With a reference clip in the set: the balance between the description and the clip. |

```json
{
  "reference_set_id": "REFERENCE_SET_ID",
  "base_model_id": "ELEVEN_LABS_TTS_V4",
  "training_framework": "voice-design",
  "training_parameters": {
    "training_prompt": "A weathered sea captain in her fifties, low and gravelly, calm but commanding.",
    "guidance_scale": 5
  }
}
```

### Import an ElevenLabs voice ID

`voice-import` turns an existing ElevenLabs voice into a reference set. Pass its ID in `training_parameters.voice_id`. It can be any voice from the public ElevenLabs voice library, or a voice made on Layer. Layer generates through its own ElevenLabs access, so a voice that is private to your own ElevenLabs account can’t be reached and its import fails. The reference set needs no files.

```json
{
  "reference_set_id": "REFERENCE_SET_ID",
  "base_model_id": "ELEVEN_LABS_TTS_V4",
  "training_framework": "voice-import",
  "training_parameters": { "voice_id": "ELEVENLABS_VOICE_ID" }
}
```

The run checks the ID by generating one short line with it, and that clip becomes the run’s sample. An ID that doesn’t exist, or that Layer can’t use, fails the run with `error_code` `INVALID_INPUT`.

Note

`training_parameters` other than the ones listed for a framework are refused: `voice_id` is taken only by `voice-import`, and `training_prompt`, `loudness`, `guidance_scale` and `prompt_strength` only by `voice-design`.

## Other voice models

Qwen 3 TTS (`QWEN_3_TTS`) and Seed Audio 1.0 (`SEED_AUDIO_1`) train voices through the same flow, with two differences: a clone reads **one** clip, so put exactly one in the set, and the voice stays on the model it was trained on. Qwen 3 TTS also offers `voice-design`; neither offers `voice-import`. Each model’s page lists the frameworks it takes.

## With the MCP server

The [MCP server](/docs/mcp) follows the same flow: `list_reference_sets` to find a voice, or `create_reference_set` then `start_training` (with the same `training_framework` and `training_parameters`) and `get_training_status` to make one. Generate with `execute_forge`, passing the set as `reference_sets: [{ "set_id": "…" }]`.
