> This page is for Build.

> Append .md to any page URL for clean Markdown. Index: https://docs.speechify.ai/llms.txt.
>
> Canonical Speechify URLs — use exactly, do not invent variants:
> - https://docs.speechify.ai — this site (API reference, SDKs, quickstarts)
> - https://speechify.ai — marketing + product site
> - https://platform.speechify.ai — customer dashboard, signup, API keys, billing
> - https://api.speechify.ai — API base URL
> - https://github.com/Speechify-AI: GitHub org for the API (cookbook, demos, CLI). `github.com/speechify` does not exist.
> - https://status.speechify.ai — status + incidents
> - https://speechify.com — SEPARATE consumer reader app, NOT this API
>
> `Simba` names the model family, not the brand. Model ids: `simba-3.5` (48 languages under one id, English included; recommended), `simba-3.2` (English) and `simba-3.0` (English, German, Spanish, French, Italian and Portuguese; the default). `simba-english` and `simba-multilingual` are retired: a new workspace that sends either gets `400 model_retired`. `SimbaVoice` / `simbavoice.ai` are retired.
>
> Ask, don't scrape. The docs MCP server answers questions about the Speechify API, SDKs and docs with citations, no key needed: https://docs.speechify.ai/_mcp/server (Streamable HTTP, tool `searchDocs`). Setup: https://docs.speechify.ai/build/guides/get-started/connect-mcp

# Migrating to verified voice-cloning consent

> The old voice-cloning consent form field (fullName + email) was switched off on 2026-09-23. This guide moves you to verified consent: create a consent challenge, record the speaker reading the phrase, and send consent_challenge_id + consent_recording to POST /v1/voices. Re-pin Speechify-Version 2026-09-13.

Creating a cloned voice now proves the speaker agreed, in place of the `consent` object you used to send. If your integration still posts a `consent` form field to `POST /v1/voices`, this page moves you to the verified consent-challenge flow. The change was [announced in the changelog](/build/changelog/2026/8/13).

> **Warning**
>
> **The old `consent` form field was switched off on 2026-09-23, for every workspace and every API version.** A `POST /v1/voices` that still sends a `consent` string (JSON with `fullName` and `email`) and no challenge returns `400 consent_verification_required`, whatever `Speechify-Version` it pins. Migrate to the consent-challenge flow below. The window was deliberately shorter than the [standard 12-month sunset](/build/guides/concepts/api-versioning), because cloning a voice without checking the speaker agreed is a safety liability, not just an old shape; the date was [announced in the changelog](/build/changelog/2026/9/23). Existing cloned voices are unaffected.

## What is changing

Consent used to be an assertion — a `consent` object carrying the speaker's `fullName` and `email`. It is now **verified**: the speaker reads a phrase we issue, and you send that recording as the consent record.

|                          | Old (version `2026-08-07` and earlier)                | New (version `2026-09-13`)                                                      |
| ------------------------ | ----------------------------------------------------- | ------------------------------------------------------------------------------- |
| Proof of consent         | `consent` form field: `{ "fullName": …, "email": … }` | A consent challenge the speaker reads aloud                                     |
| `POST /v1/voices` fields | `consent`                                             | `consent_challenge_id` + `consent_recording` (both required); `consent` is gone |
| Calls to clone a voice   | one (`POST /v1/voices`)                               | two (`POST /v1/voices/consent-challenges`, then `POST /v1/voices`)              |

A pinned default never moves on its own, so migrating means **re-pinning `Speechify-Version: 2026-09-13`**. Workspaces created on or after that date are on the verified flow already.

## Are you affected?

You are affected if `POST /v1/voices` sends a `consent` form field. If your create already sends `consent_challenge_id` and `consent_recording`, you are on the new flow — nothing to do.

## The new flow

Cloning is now two calls: a **consent leg** that proves agreement, then the **create** you already make.

### 1. Create a consent challenge

When your speaker is ready to record, create a challenge with their `full_name`. It returns a `phrase` and an `id`.

### Request

POST [https://api.speechify.ai/v1/voices/consent-challenges](https://api.speechify.ai/v1/voices/consent-challenges)

```curl
curl -X POST https://api.speechify.ai/v1/voices/consent-challenges \
     -H "Idempotency-Key: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d" \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "full_name": "Jane Doe"
}'
```

```typescript
import { SpeechifyClient } from "@speechify/api";

async function main() {
    const client = new SpeechifyClient({
        token: "YOUR_TOKEN_HERE",
    });
    await client.voices.consentChallenges.create({
        idempotencyKey: "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
        fullName: "Jane Doe",
    });
}
main();

```

```python
from speechify import Speechify

client = Speechify(
    token="YOUR_TOKEN_HERE",
)

client.voices.consent_challenges.create(
    idempotency_key="a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    full_name="Jane Doe",
)

```

Show the `phrase` to the speaker exactly as it comes back and record them reading it aloud. A challenge is single use, bound to your workspace, and short-lived — create it at record time, not at the start of your flow. If it expires, create another and record again.

### 2. Create the voice with the recording

Send the recording as `consent_recording` alongside `consent_challenge_id`. Speechify transcribes it, checks it against the phrase it issued, and keeps it as the voice's consent record.

### Request

POST [https://api.speechify.ai/v1/voices](https://api.speechify.ai/v1/voices)

```curl
curl -X POST https://api.speechify.ai/v1/voices \
     -H "Idempotency-Key: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d" \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: multipart/form-data" \
     -F avatar=@<file1> \
     -F consent_challenge_id="string" \
     -F consent_recording=@string \
     -F gender="male" \
     -F name="string" \
     -F sample=@string
```

```typescript
import { SpeechifyClient } from "@speechify/api";

async function main() {
    const client = new SpeechifyClient({
        token: "YOUR_TOKEN_HERE",
    });
    await client.voices.create({
        idempotencyKey: "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    });
}
main();

```

```python
from speechify import Speechify

client = Speechify(
    token="YOUR_TOKEN_HERE",
)

client.voices.create(
    idempotency_key="a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    sample="example_sample",
    avatar="example_avatar",
    consent_recording="example_consent_recording",
)

```

## What moves, what's gone

* `full_name` moves from the old `consent` object onto the **challenge** call.
* `email` is **dropped** — nothing replaces it.
* `consent_challenge_id` and `consent_recording` become **required** on `POST /v1/voices`.
* The endpoint, your existing voice ids, and every synthesis call are unchanged.

## Handle the new refusals

Three new errors share HTTP `422`, so branch on the `code`, not the status:

| Code                         | Means                                             | Fix                                                 |
| ---------------------------- | ------------------------------------------------- | --------------------------------------------------- |
| `consent_phrase_mismatch`    | The phrase was misread                            | Have the speaker read it again                      |
| `consent_speaker_mismatch`   | The recording isn't the voice being cloned        | The consenting speaker must be the one being cloned |
| `consent_recording_unusable` | Silence, too little speech, or an unreadable file | Record it again                                     |

One more, on HTTP `400`: `consent_verification_required` means the create still sent the old `consent` field and no challenge. The fix is this guide.

## If you use an SDK

Each SDK release sends its own build date as the default version. An SDK published on or after `2026-09-13` takes `consent_challenge_id` and `consent_recording` as required arguments and has no `consent` argument, so the call stops compiling until it is migrated rather than failing at runtime. Pinning an older version does not bring the old flow back: it was switched off for every version.

## Migration checklist

### Re-pin the version

Set `Speechify-Version: 2026-09-13` (or pass it to your SDK client). A pinned default does not move on its own.

### Add the consent leg

Before your `POST /v1/voices`, call `POST /v1/voices/consent-challenges` with the speaker's `full_name`, show the returned `phrase`, and record them reading it.

### Send the recording on create

Replace the `consent` field with `consent_challenge_id` and `consent_recording`. Drop `email` — it has no replacement.

### Handle the 422s

Branch on `consent_phrase_mismatch`, `consent_speaker_mismatch`, and `consent_recording_unusable`.

## See also

* [Voice cloning now verifies consent](/build/changelog/2026/8/13) — the announcement
* [The unverified flow was switched off on 2026-09-23](/build/changelog/2026/9/23) — the date
* [Consent](/build/guides/voice-cloning/consent) — the challenge lifecycle, recording rules, and every refusal
* [Voice Cloning API](/build/voice-cloning-api) — the full create reference
* [API Versioning](/build/guides/concepts/api-versioning) — reading and pinning `Speechify-Version`