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.

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.

Step 1 - First attempt
POST /v1/pdf
Idempotency-Key: render-req-001

{
  "html": "<h1>Invoice</h1>",
  "output": "url",
  "executionMode": "sync"
}
Step 2 - Retry with same key and body
POST /v1/pdf
Idempotency-Key: render-req-001

{
  "html": "<h1>Invoice</h1>",
  "output": "url",
  "executionMode": "sync"
}
Step 3 - Result
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.

Step 1 - Original request
POST /v1/pdf/batch
Idempotency-Key: batch-req-001

{
  "items": [
    { "clientItemId": "invoice-1", "html": "<h1>A</h1>" }
  ]
}
Step 2 - Conflicting retry
POST /v1/pdf/batch
Idempotency-Key: batch-req-001

{
  "items": [
    { "clientItemId": "invoice-2", "html": "<h1>B</h1>" }
  ]
}
Step 3 - Result
409 Conflict

Idempotency key has already been used with a different batch request

A 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.