Guides
Errors, retries, and idempotency
The LyricWinter API response envelope, error codes, request IDs, retry rules, idempotency keys, and client mutation IDs for safe retries of paid operations.
BetaUpdated
Every LyricWinter API response uses a consistent JSON envelope, and every error carries a stable code and a retryable flag. Most paid or state-changing requests support safe retries with an idempotency key or a client mutation ID. This page explains how to read errors and when it is safe to retry.
Response envelope#
Successful responses wrap the result in data:
{
"data": { "id": "8f14e45f-ceea-4e7a-9c2d-3b1a5f2e7c10" },
"request_id": "req_01J9Z3K8QF4"
}List endpoints that paginate add a pagination object next to data. The public OpenAPI document at /api/v1/openapi.json is the one exception: it is returned unwrapped.
Error format#
Errors use the same shape on every endpoint:
{
"error": {
"code": "STUDIO_DOCUMENT_CONFLICT",
"message": "Studio changed before this save could commit.",
"retryable": false
},
"request_id": "req_01J9Z3K8QF4"
}codeis stable and safe to branch on.messageis human-readable and may change.retryableistruewhen repeating the same request later can succeed.detailsis optional structured context, such as the conflicting version.request_idis also sent as thex-request-idresponse header. Include it when you contact support.
Error codes#
| HTTP status | code | Retryable | What to do |
|---|---|---|---|
| 400 | BAD_REQUEST | No | Fix the request. message names the invalid field. |
| 401 | UNAUTHORIZED | No | Send a valid, unexpired, unrevoked API key. |
| 403 | FORBIDDEN | No | The key is missing a required scope, or the resource belongs to another account. |
| 404 | NOT_FOUND | No | Check the ID. Deleted resources return 404. |
| 409 | CONFLICT or a Studio conflict code | No | Re-read the resource, then retry with the current version. |
| 429 | RATE_LIMITED | Yes | Wait for the Retry-After header, then retry. |
| 500 | INTERNAL_SERVER_ERROR | No | Retry once later; if it persists, contact support with the request_id. |
| 502 | UPSTREAM_UNAVAILABLE | Yes | A provider is unavailable. Retry with backoff. |
| 503 | SERVICE_UNAVAILABLE | Yes | Studio is temporarily unavailable. Retry with backoff. |
Studio endpoints add more specific codes. Common ones:
code | Meaning |
|---|---|
STUDIO_DOCUMENT_CONFLICT | The document changed since the version you sent. |
STUDIO_SECTION_ORDER_CONFLICT | The project's section order changed since the version you sent. |
STUDIO_IDEMPOTENCY_KEY_REUSED | A client_mutation_id was reused with a different request body. |
STUDIO_GENERATION_PRICE_CHANGED | The ElevenLabs v4 price changed before generation started. Review the updated quote, then retry. Status 409. |
STUDIO_GENERATION_PRICE_REJECTED | One or more generation targets has an invalid price. Refresh the quote before retrying. Status 409. |
IDEMPOTENCY_KEY_REQUIRED | The endpoint requires an Idempotency-Key header, for example POST /studio/exports. |
Workflow steps report their own failures in error_code and error_message on each step. For example, STUDIO_INSUFFICIENT_BALANCE means the account ran out of words during generation.
When is it safe to retry?#
- Network errors and timeouts. The request may or may not have been applied. Retry with the same idempotency key or
client_mutation_id; never generate a new one for a retry. retryable: true. Retry with exponential backoff, starting at about one second and capping at about a minute.429 RATE_LIMITED. Wait at least the number of seconds inRetry-After.409conflicts. Do not retry blindly. Re-read the resource, decide whether your change still applies, and send it with the current version and a new mutation ID.- Other
4xxerrors. Fix the request first.
Idempotency#
The LyricWinter API uses two idempotency mechanisms. Each endpoint in the API reference says which one it uses.
client_mutation_id in the body (most Studio writes). Send a new UUID for each intended change. Replaying the same ID with the same body returns the original result. Some creation endpoints signal a replay with 200 and idempotent_replay: true instead of 201. Reusing an ID with a different body returns 409.
For POST /studio/sections/{sectionId}/script, retry an uncertain response with the same body and client_mutation_id. The API verifies the original command receipt before accepting a replay, even if the document versions advanced; the response contains the current canonical document, not a historical snapshot. A changed body returns 409. If the original command can no longer be reconstructed after a capability update, the retry also returns 409 without importing again; read the document to confirm the outcome. This endpoint never starts paid work.
Idempotency-Key header (exports and some other endpoints). Send a unique key of up to 255 characters per logical request. Replaying the same key with the same body returns the original result with the response header Idempotency-Replayed: true. Reusing a key with a different body returns 409.
Polling long-running work#
Workflows and exports return 202 Accepted and keep running after the response. Poll the returned resource instead of repeating the start request:
- Poll every 2–3 seconds at first, then back off to every 5–10 seconds for long runs.
- Stop when the status is terminal. For workflows that is
completed,partially_completed,failed, orcancelled; for exports it iscompleted,failed,cancelled, orsuperseded. - Treat signed media URLs as short-lived. Fetch fresh ones when they expire instead of storing them.