Concepts
Retry safely without creating duplicate work
Idempotency lets a client retry a create request without accidentally creating the same render or batch twice. Read this page in order: what idempotency means, where it applies, how replay behaves, how conflicts happen, and how to structure retry logic.
When to use it
Use the Idempotency-Key header on create routes when the client may retry after a timeout, dropped connection, or ambiguous upstream failure.
Safe replay
The same key with the same request body reuses original work instead of creating a duplicate.
Conflict
The same key with a different request body is rejected as a conflict.
What It Is
Idempotency gives create routes a stable retry handle
Without idempotency, a timeout or dropped connection leaves the client unsure whether the API never received the request or already accepted it. With idempotency, the client retries with the same key so the API can map that retry back to the original create operation.
- Use one key per logical create action, not one key per HTTP attempt.
- Reuse that key only when the request body is meant to represent the same work.
- Generate a new key when the payload changes in a meaningful way.
- If you omit the header entirely, the create route behaves like a normal non-idempotent create call.
Supported Routes
Use idempotency on render and batch creation
The idempotency contract applies to the two routes that admit new rendering work. Both validate the Idempotency-Key header before they create new work, so a retry can reattach to the original operation instead of duplicating it.
Single render
Protect one render request from duplicate admission
Use the same key when retrying the same single render create request after a timeout, dropped connection, or ambiguous client side failure.
POST/v1/pdf
Batch render
Protect batch creation from duplicate jobs
Use the same key when retrying the same batch create request so the API can reattach to the original batch instead of enqueueing it again.
POST/v1/pdf/batch
Practical header rules are the same on both routes: surrounding whitespace is trimmed, keys longer than 255 characters are rejected, and keys cannot contain HTML markup or control characters.
Replay Behavior
The same key and same body should lead back to original work
A safe replay does not guarantee the same transport shape every time. It guarantees the create request is not admitted twice. Depending on timing, a retry can reconnect to already accepted work or replay a stored completed result.
What happens on safe replay
Single render
reattach or replay
If the original render is still queued or processing, the API can return the same accepted job path. If the original render completed, the API can replay stored output instead of enqueueing new work.
Batch create
return the existing batch job
If the key matches the same batch request body, the API resolves to the existing batch job instead of creating a second batch with duplicate items.
Read this example in order, one step at a time.
POST /v1/pdf
Idempotency-Key: render-req-001
{
"html": "<h1>Invoice</h1>",
"output": "url",
"executionMode": "sync"
}POST /v1/pdf
Idempotency-Key: render-req-001
{
"html": "<h1>Invoice</h1>",
"output": "url",
"executionMode": "sync"
}The API reuses the original render work.
If the original job is still queued or processing:
- the client reattaches to that job
If the original job already completed:
- the API can replay the stored output
Duplicate render work is not created.Conflict Behavior
The same key with a different body is a retry-contract conflict
The idempotency key is not a grouping label and it is not a reusable session token. Once a key represents one request body, changing that body under the same key becomes a conflict.
- Changing input content, output mode, or other meaningful request data under the same key is a new logical request.
- When that happens, the API rejects the request instead of guessing which body the key was supposed to represent.
- This is why the right mental model is "same key for the same work" rather than "same key for the same user flow."
This example follows the same three-step pattern. The key stays the same, but the logical work changes, so the API rejects the second request instead of guessing which version should win.
POST /v1/pdf/batch
Idempotency-Key: batch-req-001
{
"items": [
{ "clientItemId": "invoice-1", "html": "<h1>A</h1>" }
]
}POST /v1/pdf/batch
Idempotency-Key: batch-req-001
{
"items": [
{ "clientItemId": "invoice-2", "html": "<h1>B</h1>" }
]
}409 Conflict
Idempotency key has already been used with a different batch requestA second 409 path is also possible with the same body: if the first request is still establishing state, the API can return a temporary conflict and instruct a short retry with the same key.
Retry Guidance
Retry the same request with the same key, or create new work with a new key
Good idempotency behavior mostly comes down to client discipline. Persist the key long enough to survive retries and make sure the retry path does not mutate the body between attempts.
01
Create the key before the first attempt
Generate one idempotency key at the moment you decide to create a render or batch job, then keep it with the request body.
02
Reuse the key only for a true retry
Use the same key only when the outcome of the original create call is unknown or ambiguous.
03
Mint a new key for new work
If the body changes or the user is intentionally creating new work, mint a new key instead of reusing the old one.
Next Steps
Use the concept here, then move into the route or guide you need
Once the retry model is clear, continue with one of these canonical pages for exact request contracts or implementation patterns.
Need the render contract?
Open single render reference
See where the Idempotency-Key header fits into the single render request contract and how status codes behave around retries.
Need batch create behavior?
Open batch rendering reference
Use the batch page for create semantics, existing-job reuse, and status polling after accepted work.
Need implementation guidance?
Open retries and idempotency guide
Move from the concept into client side retry strategy, backoff, and production ready request handling.