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

# Idempotency

> How to make write requests safe to retry without double-charging or duplicating work.

The API is metered — a retry after a network timeout must not double-charge your wallet or create the same resource twice. Send an idempotency key on writes and a retry becomes safe by construction, rather than something you have to reason about at the call site.

***

## How it works

Send an `Idempotency-Key` header on any `POST`, `PUT`, `PATCH`, or `DELETE`. Use a UUID v4 per logical operation — the same value for every attempt of that operation, a new value for the next one.

```bash theme={null}
curl -X POST "https://connect.jobo.world/api/some-write-endpoint" \
  -H "X-Api-Key: $JOBO_API_KEY" \
  -H "Idempotency-Key: 6c9b1e2a-8f3d-4b7a-9c1e-2a8f3d4b7a9c" \
  -H "Content-Type: application/json" \
  -d '{"...": "..."}'
```

```python theme={null}
import uuid
import httpx

key = str(uuid.uuid4())

response = httpx.post(
    "https://connect.jobo.world/api/some-write-endpoint",
    headers={
        "X-Api-Key": "YOUR_API_KEY",
        "Idempotency-Key": key,
    },
    json={"...": "..."},
)
```

<Note>
  The stored result is keyed on **(your user, the key, the route)**. The same key on a different route is a different entry, so reusing a key across two distinct endpoints does not collide. Reusing a key for a genuinely different request body on the *same* route returns the **original** response rather than performing the new one — the key, not the body, is authoritative.
</Note>

## What gets stored, and for how long

* Results are retained **24 hours**.
* **Only `2xx` responses are stored.** A failed request does not burn the key — retrying after an error genuinely re-executes, rather than replaying the failure.
* A replay returns the original status and body, plus the `X-Idempotent-Replay` header. Check that header to tell a replay apart from a fresh execution — see [Response headers](/docs/response-headers).
* Idempotency does not apply to `GET`. Those are already safe to repeat and ignore the header if sent.

## Worked example: retry after a timeout

The client sends a request, the connection drops before the response arrives, and the client retries with the **same key**.

```bash theme={null}
# Attempt 1 — times out client-side; the server may have already completed it
curl -X POST "https://connect.jobo.world/api/some-write-endpoint" \
  -H "X-Api-Key: $JOBO_API_KEY" \
  -H "Idempotency-Key: 6c9b1e2a-8f3d-4b7a-9c1e-2a8f3d4b7a9c" \
  -H "Content-Type: application/json" \
  -d '{"...": "..."}'

# Attempt 2 — same key, same body: replays attempt 1's result, no new work done
curl -si -X POST "https://connect.jobo.world/api/some-write-endpoint" \
  -H "X-Api-Key: $JOBO_API_KEY" \
  -H "Idempotency-Key: 6c9b1e2a-8f3d-4b7a-9c1e-2a8f3d4b7a9c" \
  -H "Content-Type: application/json" \
  -d '{"...": "..."}'
```

```
HTTP/1.1 200 OK
X-Idempotent-Replay: true
```

`X-Idempotent-Replay` present means the second call did no new work and was not billed again — it returned attempt 1's stored result.

## Common mistakes

<AccordionGroup>
  <Accordion title="Generating a new key on every retry" icon="rotate">
    This defeats the purpose entirely — each retry looks like a brand-new operation and can duplicate work or double-charge. Generate the key **once** per logical operation, before the first attempt, and reuse it for every retry of that same attempt.
  </Accordion>

  <Accordion title="Reusing a key for a different request body" icon="clone">
    The key, not the body, is authoritative. Reusing `6c9b1e2a-...` for an unrelated request on the same route returns the **original** stored response, not the result of the new body. Mint a fresh key per distinct operation.
  </Accordion>

  <Accordion title="Assuming a failed attempt burned the key" icon="ban">
    Only `2xx` responses are stored. If attempt 1 returned a `4xx` or `5xx`, the key is still free — retrying with the same key genuinely re-executes the request rather than replaying the failure.
  </Accordion>

  <Accordion title="Sending it on GET" icon="magnifying-glass">
    `GET` requests are already safe to repeat. The header is accepted but has no effect there.
  </Accordion>

  <Accordion title="Expecting it to work past 24 hours" icon="clock">
    Stored results expire after 24 hours. A retry sent after that window with the same key executes as a new request rather than replaying.
  </Accordion>
</AccordionGroup>

## The Auto Apply exception

`POST /api/auto-apply/applications` is designed **not** to use the shared 24-hour cache. It is a contract preview and currently returns `503 auto_apply_coming_soon` for every request regardless of key — see [Errors](/docs/errors). At launch it will track its own durable idempotency record and return dedicated conflict codes instead of a silent replay:

| `code`                      | Meaning                                                         |
| --------------------------- | --------------------------------------------------------------- |
| `idempotency_key_reuse`     | The key was already used to completion for a different request. |
| `idempotency_key_in_flight` | A request with this key is still being processed.               |

<Warning>
  Every other write endpoint follows the shared 24-hour cache described above. Auto Apply application creation is the one exception, and its own idempotency record is not live yet.
</Warning>
