Create Consent Challenge
Start the consent check for a voice clone.
Returns a phrase for the speaker to read aloud and an id that identifies this challenge. Show the phrase to the speaker exactly as returned, record them reading it, then send the recording and the id to POST /v1/voices, which verifies the recording against the phrase and against the voice sample being cloned, then keeps it as the consent record.
A challenge is single use, is bound to the workspace that created it, and expires at expires_at - it is proof that a speaker was in front of a microphone just now, so create it when you are ready to record, not at the start of your flow. If it expires, create another one and record again.
Challenge creation is rate limited per workspace at a few dozen per hour, far more tightly than the rest of the voice surface, because each one precedes a person recording themselves - mint it when your speaker is ready, not speculatively. Read the live ceiling off RateLimit-* rather than hard-coding it. On a 429, always honour Retry-After rather than a fixed backoff of your own: the wait is measured in minutes and can run to most of an hour. RateLimit-* are omitted rather than reporting a bucket that is not the one refusing.
Like POST /v1/voices, a challenge requested from a jurisdiction where voice cloning is not available returns 403 voice_cloning_unavailable_in_region and issues no phrase. The location is read from the IP address that sends the request, which is your server’s when your backend calls the API.
Authentication
Enter your API key with the Bearer prefix, e.g. ‘Bearer sk_…’.
Headers
A client-generated key (an opaque string, max 255 chars) that makes a
side-effect POST safe to retry: the server runs the operation exactly
once and replays the first response (its status and body) for 24 hours.
Reusing a key with a different request body, or while the first request
is still in flight, returns 409 idempotency_conflict. A replayed
response carries the Idempotent-Replayed: true header.
Request
Full name of the person consenting to have their voice cloned. Speechify binds it to the challenge and stores it with the consent record, so the create that consumes the challenge does not carry it and cannot change it.
At most 120 bytes once UTF-8 encoded, which is 120 characters of
Latin script but around 40 of Chinese, Japanese or Korean. Stated in
bytes rather than as a maxLength because the two only agree on
single-byte scripts, and a character count that never over-accepts
would have to refuse Latin names at 30. A name over the limit comes
back as validation_failed reporting its measured length.
Response headers
Unique identifier for this request, present on every response (2xx and
non-2xx alike). If the caller sends a Speechify-Request-Id request
header the server echoes it back (sanitized and length-capped) so one
logical request can be traced end-to-end; otherwise the server generates
a fresh value. Log it on every response and quote it in support requests
- it is the stable handle that ties your observation to Speechify’s
server-side logs, and it matches the
request_idfield in the error envelope.
The legacy alias X-Request-ID carries the same value and is still
accepted on requests, until 2027-07-24. Prefer the un-prefixed name
(RFC 6648).
Request-rate budget: the maximum number of requests in the current
window (the bucket capacity). The IETF-draft un-prefixed name; the
legacy alias X-RateLimit-Limit carries the same value. Rides every
response.
Request-rate budget: requests left in the current window. Legacy
alias: X-RateLimit-Remaining.
Request-rate budget: integer delta-seconds until the window fully
refills (same unit as Retry-After). Legacy alias:
X-RateLimit-Reset.
Response
Identifier for this challenge, sent back as consent_challenge_id
on the create. Treat it as an opaque string - the format is not part
of the contract and will not stay stable.
The sentence the speaker must read aloud. Show it exactly as returned - the recording is transcribed and matched against this text, so re-wording, re-casing or re-punctuating it will fail the check.
When the challenge stops being usable. This is the only authority on the window - do not hard-code a duration. Past it, create a new challenge and record the new phrase.