Idempotent generation requests

Use the optional Idempotency-Key header on POST /v2/generate to identify one intended generation. If a request times out or two copies arrive concurrently, reusing the same key and inputs prevents another generation and duplicate paid work for that action.

This contract covers JSON and multipart requests, including text-to-speech inputs, segments, and dubbing through dubParams. It does not extend this guarantee to other endpoints, such as batch creation or standalone text-to-speech.

Send a key with the first request

Create a unique key for each intended action, such as a UUID, and save it with the request inputs before sending the request. Keep that key across retries, reconnects, and application restarts. Do not generate a key inside your retry loop.

Keys are case-sensitive and scoped to your organization, across its members and API keys. Send exactly one header with 1–128 characters matching [A-Za-z0-9._~-]+. Empty, duplicate, or malformed headers return HTTP 400 with INVALID_IDEMPOTENCY_KEY.

# Save this key with this action. Reuse it and the same body for every retry.
IDEMPOTENCY_KEY="d9bc7a6e-73cd-4d9a-8a2b-2e6019846044"
curl --include --request POST 'https://api.sync.so/v2/generate' \
--header "x-api-key: $SYNC_API_KEY" \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
--header 'Content-Type: application/json' \
--data '{
"model": "lipsync-2",
"input": [
{"type": "video", "url": "https://assets.sync.so/docs/example-video.mp4"},
{"type": "audio", "url": "https://assets.sync.so/docs/example-audio.wav"}
]
}'

The first accepted request returns HTTP 201. An equivalent retry returns HTTP 200, the same generation ID in its current state, and Idempotency-Replayed: true. A replay does not mean rendering is complete: inspect status, then poll the generation or use webhooks.

For direct uploads, use the same header and resend the original files and form values:

# This is a separate action from the JSON example, so it has a different key.
UPLOAD_ACTION_KEY="7ac28f5d-27a1-4212-bf73-85d7c7a9c8ea"
curl --include --request POST 'https://api.sync.so/v2/generate' \
--header "x-api-key: $SYNC_API_KEY" \
--header "Idempotency-Key: $UPLOAD_ACTION_KEY" \
--form 'model=lipsync-2' \
--form 'video=@./video.mp4;type=video/mp4' \
--form 'audio=@./audio.wav;type=audio/wav'

Let your HTTP client set the multipart boundary. Nested input, options, segments, and dubParams fields must be JSON strings. See Create Generation with Files for upload limits and field details.

Handle responses

HTTP statusResultWhat to do
201First submission acceptedSave the generation ID and follow its status.
200Equivalent accepted replay; Idempotency-Replayed: trueUse the original generation’s current state. No new generation is created.
400INVALID_IDEMPOTENCY_KEYCorrect the header format.
409IDEMPOTENCY_KEY_CONFLICTThe key is bound to different inputs or context. Restore the original request for a retry; use a new key only for a distinct intentional action.
409IDEMPOTENCY_IN_PROGRESSWait for Retry-After: 2, then retry the same key and payload.
503IDEMPOTENCY_OUTCOME_UNKNOWNAcceptance is uncertain. Keep the key; poll generationId when provided, or retry the same request with backoff to check for reconciliation. Contact support if it remains unresolved.
503IDEMPOTENCY_UNAVAILABLENew keyed submissions are not enabled. Keep the same key and retry later; do not remove it to bypass protection.
410IDEMPOTENCY_GENERATION_DELETEDThe original generation was deleted. The key remains bound until expiry and cannot recreate it.

An accepted generation that later fails still replays with HTTP 200 and its failed status. If you intentionally start a replacement after that confirmed failure, use a new key. A definitive submission failure after paid preparation begins is recorded; equivalent retries return that error rather than repeating the work.

A timeout, disconnect, or failed status lookup does not prove that the original job was rejected. Never switch to a fresh key or an unkeyed request to bypass an unknown outcome. Some provider outcomes require support reconciliation.

If you contact support, include the generation ID when available, request ID when provided, organization, and approximate submission time. Do not send your API credential.

Keep the payload equivalent

The comparison includes inputs, model, options, segment order, TTS scripts and voice settings, dubbing settings, webhook URL, output filename, and effective project/source/mode/workflow context.

  • JSON object field order does not matter. Array and segment order does.
  • Supported model aliases and existing request transformations are normalized.
  • Files compare by their bytes, media type, and extension. Multipart boundaries and base filenames do not matter.
  • URLs compare as supplied, including query strings. A refreshed signed URL counts as a change. Prefer stable asset IDs when preparing a new action.
  • Keep the same input representation on retries. Replacing a file upload with a URL or asset ID conflicts, even if it points to the same media.
  • Keep any x-sync-source and x-sync-mode headers consistent. API-key requests default to the api source; switching to session authentication without preserving that context can conflict.

Credentials do not form part of the key, but every request must authenticate and have access to the generation in the same organization. Another organization may use the same key independently.

Retention and compatibility

Keys remain protected for seven days from initial acceptance, and longer while the generation is active or the submission outcome is unresolved. Expired, resolved keys may be reused, so do not rely on indefinite replay. Use unique keys for new actions.

Validation or admission failures known to occur before provider work and generation creation release the claim. You can correct the request and submit again after such a confirmed rejection. Recorded post-preparation failures and unknown outcomes remain bound.

Requests without this header retain their existing behavior and do not gain duplicate-submission protection. Once an action has been submitted with a key, keep sending that key on every retry. Replays skip media preparation and new-generation balance/concurrency checks; they do not bypass authentication or generation access checks.