# Create a voice

`POST https://api.vidmoat.com/v1/speech/voices`

- Scope: `speech.voices.write`
- Cost: Configured enrollment credits

Enroll an authorised recording. Validates the audio, extracts reusable conditioning and synthesizes an actual preview before marking the voice ready.

> **Note.** Show the current consent text from capabilities and obtain the person’s explicit authorization. A checkbox is not identity verification. Agents cannot grant consent for someone.

### Form fields

- `audio` (file, required): WAV, MP3, M4A or WebM, maximum 25 MB, 10 to 120 seconds, a sample rate of 16 kHz or higher, mono/stereo and at least six seconds of detected speech.
- `name` (string, required): 1 to 60 characters.
- `description` (string, optional): Up to 300 characters.
- `language` (enum, optional, default `en`): Recording language. One of `en`.
- `consent` (boolean, required): The literal multipart value true.
- `consentVersion` (string, required): Current consent.version from capabilities.

## Response

Returns `202`.

> **Tip: With a test key.** Test keys cannot enroll or run inference; use a live key. No mock personal voice is returned.

## Errors

Besides the errors any request can get ([authentication](https://developer.vidmoat.com/developer/docs/authentication#when-a-request-is-refused), [rate limits](https://developer.vidmoat.com/developer/docs/rate-limits)):

| Status | Code | When |
| --- | --- | --- |
| 409 | [`conflict`](https://developer.vidmoat.com/developer/docs/errors#conflict) | The idempotency key belongs to different inputs, or the job is not in the required state. |
| 503 | [`unavailable`](https://developer.vidmoat.com/developer/docs/errors#unavailable) | Inference, private storage, configured pilot billing or the limiter is unavailable. Nothing new is charged before admission. |

## Request examples

```bash curl
curl --fail-with-body "https://api.vidmoat.com/v1/speech/voices" -H "Authorization: Bearer $VIDMOAT_KEY" -H "Idempotency-Key: $REQUEST_ID" -F "audio=@my-voice.wav" -F "name=My voice" -F "language=en" -F "consent=true" -F "consentVersion=speech-voice-2026-10-22"
```

```js Node
import { readFile } from 'node:fs/promises';

const form = new FormData();
form.append('audio', new Blob([await readFile('my-voice.wav')]), 'my-voice.wav');
form.append('name', 'My voice');
form.append('language', 'en');
form.append('consent', 'true');
form.append('consentVersion', 'speech-voice-2026-10-22');

const res = await fetch('https://api.vidmoat.com/v1/speech/voices', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.VIDMOAT_KEY}`,
  },
  body: form,
});
const data = await res.json();
if (!res.ok) throw new Error(data.error?.message ?? `HTTP ${res.status}`);
console.log(data);
```

```python Python
import os
import requests

res = requests.post(
    "https://api.vidmoat.com/v1/speech/voices",
    headers={"Authorization": f"Bearer {os.environ['VIDMOAT_KEY']}"},
    files={"audio": open("my-voice.wav", "rb"), "name": (None, "My voice"), "language": (None, "en"), "consent": (None, "true"), "consentVersion": (None, "speech-voice-2026-10-22")},
    timeout=60,
)
res.raise_for_status()
print(res.json())
```

## Example response

```json
{
  "id": "11111111-1111-4111-8111-111111111111",
  "voiceId": "22222222-2222-4222-8222-222222222222",
  "kind": "generate",
  "status": "queued",
  "refunded": false,
  "pipeline": "openvoice-v2-melo-1",
  "result": null,
  "error": null
}
```

---

Source: https://developer.vidmoat.com/developer/docs/api/speech/create-voice
Previous: [Direct a narration draft](https://developer.vidmoat.com/developer/docs/api/speech/direction.md)
Next: [Get a personal voice](https://developer.vidmoat.com/developer/docs/api/speech/get-voice.md)
All documentation: https://developer.vidmoat.com/llms-full.txt
