> ## Documentation Index
> Fetch the complete documentation index at: https://docs.andteam.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Retries and versions

> Idempotency-Key on every POST, and ETag with If-Match on every PATCH.

A network drops, a worker restarts, a webhook handler runs twice. Two headers make a write safe to
repeat and safe to race.

## Idempotency-Key on every POST

Every POST needs an `Idempotency-Key` header: a value you choose that is unique for the request, such
as a UUID. A POST without one answers [`idempotency-key-missing`](https://docs.andteam.com/api/problems/idempotency-key-missing).
The withdraw endpoints need it too.

andteam remembers a key for **24 hours**, per API key, with a hash of the body.

| You send | You get |
| - | - |
| The same key and the same body, after the first call finished | The first answer again, with `Idempotent-Replayed: true`. Nothing new is created. |
| The same key and a different body | 422 [`idempotency-key-reused`](https://docs.andteam.com/api/problems/idempotency-key-reused) |
| The same key while the first call is still running | 409 [`idempotency-in-progress`](https://docs.andteam.com/api/problems/idempotency-in-progress) |

So retry a request that timed out with the **same key and body**. A new request gets a new key. On
`idempotency-in-progress`, wait a moment and retry with the same key.

```python theme={null}
import time, uuid, requests

key = str(uuid.uuid4())          # one per logical request, kept across retries
while True:
    r = requests.post(url, json=body, headers={**H, "Idempotency-Key": key})
    if r.status_code == 409 and r.json()["type"].endswith("idempotency-in-progress"):
        time.sleep(1)            # the first call is still running
        continue
    break
```

## ETag and If-Match on PATCH

`GET /v1/employees/{id}` returns a strong `ETag`, and so does every request resource. A PATCH needs
`If-Match` with it:

| You send | You get |
| - | - |
| No `If-Match` | 428 [`precondition-required`](https://docs.andteam.com/api/problems/precondition-required) |
| A stale `ETag` | 412 [`precondition-failed`](https://docs.andteam.com/api/problems/precondition-failed). Nothing is written. |
| The current `ETag` | 200 with the employee and a new `ETag` |

On a 412, read the record again and apply your change to what it holds now.

## Job and pay changes

A job change or a pay change needs no `If-Match`. It is measured against a base: the employee's current
assignment, or the current pay entry. If that base moves while the request waits, the final approval
refuses it. You may still send `If-Match` with the employee's `ETag` to be refused early.

See [Making changes](https://docs.andteam.com/guides/writes.md) for the writes themselves, and [Errors](https://docs.andteam.com/errors.md) for every type.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.