# Retries and rate limits

Retry writes safely with an Idempotency-Key, and stay within the request limits.

- HTML docs: https://www.invunion.com/knowledge-base/api/idempotency/
- Markdown docs: https://www.invunion.com/knowledge-base/api/idempotency.md

## Idempotency-Key

Every write that a tenant API key can call (POST, PUT, PATCH, and DELETE on counterparties, payment methods, transactions, invoices, matches, import jobs, and ingest) accepts an `Idempotency-Key` header. Generate one key per logical operation, for example a UUID, and send the same key when you retry that operation after a timeout or network error.

```bash
curl --request POST \
  --url https://api.invunion.com/api/v1/invoices \
  --header 'authorization: Bearer uk_live_YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --header 'Idempotency-Key: 5f0c2b8e-invoice-INV-2026-0042' \
  --data '{ "invoiceNumber": "INV-2026-0042", "...": "..." }'
```

- Same key and same body after the first request succeeded: the stored response is returned with the header `Idempotent-Replayed: true`, and nothing is written again.
- Same key while the first request is still running: 409 with `code` `idempotency_in_progress`. Wait and retry. A request stuck for more than 5 minutes is treated as abandoned and can be taken over.
- Same key with a different body: 422 with `code` `idempotency_key_reuse`. Use a new key for a new request.
- Only successful (2xx) responses are stored. After an error, the key is released and you can retry with it.
- A key is 8 to 255 characters: letters, digits, `.`, `_`, `:`, or `-`. It is scoped to your tenant and to the method and path, and kept for 24 hours.

Without the header, a retried write can run twice. Most creates are protected by a unique code anyway (`invoiceNumber`, `account_code`, `sourceId`), but matches and import jobs are not.

## Rate limits

- All endpoints: 100 requests per minute per API key, and 100 per minute per IP address.
- Create import job: 20 requests per minute per key, on top of the general limit.
- Ingest transactions (deprecated): 10 requests per minute per key, on top of the general limit.

Over a limit, the API answers 429 with `Retry-After` in seconds and the `RateLimit-*` headers. Wait that long before retrying. To move many records, send them in one Create import job (up to 10,000 items) or one bulk import call instead of one request each.

## Retrying errors

- 429 and 503: wait for `Retry-After`, then retry.
- 500: retry with exponential backoff and the same `Idempotency-Key`. The body includes `correlationId`; quote it when you contact support.
- 400, 403, 404, 409, and 422: fix the request first. Retrying it unchanged returns the same error.
