Idempotency
Network calls fail and time out. When that happens to a request that spends credits or creates a resource, retrying it naively can spend those credits twice. The Idempotency-Key header solves this: send the same key on a retry, and you get back the exact same response as the original call — without it running again.
How it works
Add an Idempotency-Key header with any client-generated unique string (a UUID is a good choice) to a request that supports it:
curl -X POST https://imagetotable.ai/api/v1/batches/260716-4K9P/process \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f3e9a2c-9b41-4e6a-8c3d-2f1a5b6c7d8e" \
-d '{"fields": [{"name": "invoice_number"}, {"name": "total_amount"}]}'The key is stored per (your account, key value, endpoint) for 24 hours. If the exact same request (same endpoint, same key) arrives again within that window, the original response — same status code, same body — is replayed, and the operation itself does not run a second time. That means a process call retried with the same key does not deduct credits twice, and a documents upload retried with the same key does not create a second document.
Requests without an Idempotency-Key header behave exactly as before — nothing is stored, nothing is replayed. The header is entirely opt-in.
Which endpoints support it
Only endpoints that spend credits or create a resource accept Idempotency-Key — there's no benefit to it on a read-only GET, since repeating a read is always safe on its own:
POST /documents(document creation)POST /batches/{batch_name}/process(starts processing, deducts credits)POST /documents/{document_id}/bbox(triggers the paid bbox annotation step)
Reusing a key with different parameters
An Idempotency-Key is meant for retrying the exact same request, not as a general-purpose label. If you reuse a key you've already used, but this time with different request parameters — a different batch_name, a different request body — the API does not silently replay the old response (which would be wrong for a different request) and does not guess which one you meant. It returns a 400 idempotency_key_reused error instead. Generate a fresh key whenever the request is genuinely different, even if it targets the same kind of operation.
Example: a replayed response
The second call below, made with the same key as a call that already succeeded, returns the identical body and status code as the first — it does not deduct credits again:
{
"batch_name": "260716-4K9P",
"queued": 2,
"quality": "fast",
"webhook_registered": false
}What gets recorded
Only successful (2xx) responses are saved for replay. If the original call failed — a validation error, insufficient credits, anything non-2xx — nothing is recorded, and retrying with the same key simply attempts the operation fresh. This is deliberate: an error is exactly the situation where you want your retry to actually try again, not get the same failure played back until the key expires 24 hours later.