API v1 preview

# Idempotent requests

Retry the same operation without creating duplicates.

## Protect a request from duplicates

Create a unique Idempotency-Key before the first request and save it with the body. Reuse both after a timeout or lost response. Do not generate a new key inside a retry loop.

Retry header

http

```
Idempotency-Key: 6e94ce83-0244-4d6c-93ba-f8b2687311d6
```

| Endpoint | Key |
| --- | --- |
| Create an order / Pay an order | Required |
| Create a file / Create a model / Replace a model file / Calculate prices / Create a quote / Cancel an order | Optional; recommended |
| GET | Not needed |
| Update a model | No key replay; use If-Match and reconcile with GET after a lost response. |
| Delete a model | Retrieve the model after a lost response; repeated calls may return 404. |

## Key lifetime and scope

- Use 1–255 visible ASCII characters; a random UUID is suitable.
- Every public POST accepts a key; order creation and payments require one. Keys are scoped to account, HTTP method and path for 24 hours from first use.
- The same parsed JSON body replays saved response values with `Idempotent-Replayed: true`. Object key order and whitespace can differ.
- A changed body returns `409 idempotency_key_reused`. Successful replays do not consume another creation quota slot.
- Without a key, or after its retention window, a retry may create a duplicate. Reconcile the resource before starting a new operation.

## What a replay returns

| Resource | Behavior |
| --- | --- |
| File | Original instructions and expiry, even after linking. Replay does not renew upload permission. |
| Model | Original creation snapshot, even after deletion. GET returns current availability and results. |
| Model file replacement | Original replacement snapshot. No duplicate file revision, even after another edit. |
| Quote | Original creation snapshot, including its original status. GET reports current expiry. No new rates or extended expiry. Failed admitted attempts also bind the key to the body. |
| Order | Original order snapshot, even after cancellation. GET returns current state. |
| Payment | Original payment action, which may include an expired URL. GET the order for current state. |
| Cancel order / Calculate prices | Original successful snapshot. GET the order for later refund progress; calculate with a new key for fresh prices. |

A concurrent request with the same key returns 409 idempotency_request_in_progress and Retry-After. Wait that many seconds, then retry the same key and body. Dependency failures return 503 with Retry-After; unsuccessful attempts can be retried and keep their body binding. Never create another order to recover an uncertain payment.

API v1 preview

Source: https://ottomfg.com/docs/idempotency
