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

# Idempotent Requests

> How an idempotency key lets you retry inference calls without paying for the same work twice

## The problem retries create

A `POST` that times out or returns a `5xx` leaves you guessing. The server may have received the request and run the inference, or it may not have. Retrying blindly risks running and billing the same job twice. Not retrying risks dropping a job that never actually completed.

An idempotency key removes the guesswork. You attach a key to the first attempt, Valar stores the response under that key, and every later attempt with the same key returns that stored response instead of running inference again. Retrying becomes safe across flaky networks, timeouts, and ambiguous `5xx` responses.

## How it works

Send the key in the `Idempotency-Key` request header. Use one key per logical request (a UUID or any unique string up to 255 characters) and send the same key on the first attempt and every retry.

<CodeGroup>
  ```python theme={"system"}
  from openai import OpenAI

  client = OpenAI(
      base_url="https://api.valarhq.ai/v1", # or read OPENAI_BASE_URL from the environment
      api_key="YOUR_VALAR_API_KEY",  # or read OPENAI_API_KEY from the environment
  )

  response = client.responses.create(
      model="zai-org/GLM-5.2-FP8",
      input="Summarize this document.",
      extra_headers={"Idempotency-Key": "order-9f8e7d6c"},
  )
  ```
</CodeGroup>

The first call reserves the key and runs the job. Each subsequent call with that key lands on the same reservation and returns the stored response without re-running inference. The key only earns its keep once you actually retry.

## Rules that govern a reservation

<ParamField path="Reservation scope" type="(organization, API key, idempotency key)">
  A reservation is identified by all three together. The same key under a different API key is a separate reservation. Rotating API keys therefore won't break replay, but the same idempotency key won't dedupe across two different API keys.
</ParamField>

<ParamField path="Body fingerprint" type="SHA-256">
  Valar fingerprints the request body. Reusing a key with a meaningfully different body returns `400 idempotency_error` rather than the earlier response, which prevents a retry from silently returning the wrong answer after the client changed the request.
</ParamField>

<ParamField path="Validation failures" type="key not consumed">
  Any `4xx` raised before the reservation is written leaves the key unreserved. You can fix the body and retry under the same key.
</ParamField>

<ParamField path="Replays reflect live state" type="not a frozen copy">
  A replay returns the current state of the underlying response record. For background requests the `status` tracks the task's latest transition, such as `queued` → `in_progress` → `completed`.
</ParamField>

## Deciding when to retry

Reach for the same idempotency key whenever the outcome is ambiguous. Don't retry when the work genuinely failed.

| Signal                  | What it means                                                   | What to do                                |
| ----------------------- | --------------------------------------------------------------- | ----------------------------------------- |
| Network error / timeout | Ambiguous - the server may or may not have received the request | Retry with the same idempotency key       |
| `5xx` on a POST         | Transient server-side failure                                   | Retry with the same idempotency key       |
| `429` + `Retry-After`   | Rate limit                                                      | Wait the `Retry-After` value, then retry  |
| `body.status: "failed"` | Inference genuinely failed                                      | Investigate the cause; do not blind-retry |

## See also

* [Sending Requests at Scale](/requests_at_scale) - the batch and background workflows where idempotent retries pay off most.
* [Webhooks](/webhooks) - pair idempotent retries with webhook delivery so a client can re-drive submission without triggering another inference run.
