Idempotency
Send an Idempotency-Key with every POST, PATCH or DELETE and a retried request replays the original response instead of running twice.
Networks fail between “the request was sent” and “the response arrived”. Without protection, a retry after such a failure creates a second product, queues a second billable task or submits a second import. The Idempotency-Key header removes that risk.
curl https://api.veeton.com/v1/tasks/beautifier \ -H "Authorization: Bearer $VEETON_KEY" \ -H "Idempotency-Key: 4d1b9f6e-6c2a-4c1e-9c3f-2a7e8b0d1f22" \ -H "Content-Type: application/json" \ -d @task.jsonAny opaque string up to 255 characters works; a UUID per logical operation is the usual choice. Keys are scoped to your organization and expire after 24 hours.
What a key does
| Situation | Result |
|---|---|
| Same key, same request | The stored response is replayed verbatim, with Idempotent-Replayed: true. |
| Same key, different method, path or body | 422 idempotency_key_reused. Use a fresh key per operation. |
| Same key while the first request is still running | 409 request_in_progress. Retry after it completes. |
| No key | The request runs normally. The header is opt-in. |
GET requests are inherently idempotent and are never tracked.
Retrying after an error
A 4xx is a real outcome and is replayed like any other: a rejected request is deterministic and replaying it costs you nothing.
A 5xx is not stored. The key is released, so retrying with the same key genuinely re-runs the request instead of replaying the failure. The flip side: if the operation had already taken effect before the error, a retry can produce a second one. That is the same trade-off Stripe makes, and the better half of it; the alternative is an operation you can never complete.
If a request stops responding entirely (a crash on our side before anything was recorded), the key frees itself a few minutes later. A later retry is never permanently blocked.