A render costs a credit and produces a document, so a retry after a timeout must not charge twice or mail two copies of the same invoice. Send an Idempotency-Key and the first request under that key becomes authoritative: any retry replays its exact response without rendering again.
Using it
Generate one key per operation you want to happen at most once — your invoice id, order id or a UUID — and send it on every attempt of that operation.
curl -X POST https://api.pixldocs.com/v1/render \
-H "Authorization: Bearer pxd_YOUR_KEY" \
-H "Idempotency-Key: invoice-2026-0042" \
-H "Content-Type: application/json" \
-d '{"template_id":"YOUR_TEMPLATE_ID","format":"pdf","data":{"total":"1,240.00"}}'Supported on POST /v1/render, POST /v1/renders and POST /v1/renders/batch. Keys are scoped to your workspace and to a single endpoint, and expire 24 hours after first use. Maximum 255 characters — a longer key is refused with 400 invalid_idempotency_key before anything renders.
What each retry does
- Same key, same body, first call finished — you get the stored response, byte for byte, with
X-Pixldocs-Idempotent-Replay: true. No render runs and no credit is used. For an async call that means the same jobid, so you never end up with two jobs. - Same key, first call still running —
409 idempotency_in_progresswith aRetry-After. Wait and retry the same key. - Same key, different body —
422 idempotency_key_reuse. Nothing renders. Use a new key for a new request. - The first call failed — the key is released, so retrying it renders normally rather than replaying an error forever.
- No key sent — nothing changes: every call renders and bills.
A replayed POST /v1/render returns the original file with its original Content-Type, not a JSON pointer to it, so retry paths need no separate branch in your code.
A JSON reply (async, or save_to_renders) replays with every value identical — same id, same url, same page count. The key order inside the object can differ, so compare fields rather than the raw string.
Seeing your retries
Every replay also carries X-Pixldocs-Idempotent-Replay-Count — how many times that key has been served from store, including the current call. A count climbing past what you expect means your client is retrying more than you think, not that we rendered more than once.
The key that produced a document is recorded on its archive row: open any render in Renders and the panel shows render_id, request_id and idempotency_key. A row with no key was sent without one, so a repeat of that call would render and bill again.
Reading the request id
X-Pixldocs-Request-Id: e7e25e2a-f090-4e98-85c5-01a08ba0fb5aPresent on every render response, success or failure. Store it against your own operation key — it is the id support needs to find a render. For anything billable, also consider save_to_renders: true: the render lands in your Renders history with a stable id and hosted URL you can look up instead of re-rendering. For a large job use POST /v1/renders/batch once rather than N calls with client-side retry loops.