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

# Idempotency

> Retry a submission safely so a network failure never creates a duplicate job.

Idempotency lets you retry a `POST /humanize` request without creating a second job. When a request times out or a connection drops, you often can't tell whether the server received it. Retrying blindly risks submitting the same text twice, which creates a duplicate job and charges API words a second time. An idempotency key removes that risk: Wibble recognizes the repeated request and returns the original job instead of starting a new one.

## The Idempotency-Key header

To make a submission retry-safe, send an `Idempotency-Key` header on `POST /humanize`. The key is a string of up to 256 characters that you choose.

Generate one key per logical request and reuse it across every retry of that request. A version 4 UUID works well because it's unique without coordination.

```bash theme={null}
Idempotency-Key: 3f9a1b6c-2d4e-4a7f-8c10-9e2b5d7a1f00
```

<Note>
  The key identifies the request, not the text. Use a fresh key for each new submission you want to process, even when the text is identical.
</Note>

## Behavior

Wibble compares the key and the request body to decide how to respond.

| Key    | Body                        | Response                                                                                |
| ------ | --------------------------- | --------------------------------------------------------------------------------------- |
| New    | Any                         | `202` — a new job is created.                                                           |
| Reused | Identical                   | `200` — the original job is returned, with no new job and no additional words reserved. |
| Reused | Different (within 24 hours) | `409 idempotency_conflict`.                                                             |

A successful retry returns HTTP `200` rather than `202` because the job already exists. The job object is the same one your first request created, so you can read its `id` and `status` exactly as you would on the first call.

### Conflicts

If you reuse a key with a different body within the retention window, Wibble returns `409 idempotency_conflict`. This protects you from accidentally attaching two different submissions to the same key. The response names the job the key already belongs to and when the key frees up.

```json theme={null}
{
  "error": {
    "code": "idempotency_conflict",
    "message": "This Idempotency-Key was already used with a different request body."
  },
  "existing_job_id": "7b42f1d6-0a8c-4e8f-9a21-c6d9f47c2b10",
  "expires_at": "2026-06-15T12:00:00.000Z"
}
```

To resolve a conflict, send the request with a new idempotency key, or retry with the original body that the key was first used for.

## Retention

Wibble retains each idempotency key for 24 hours, starting from the first request that used it. During that window, a matching retry returns the original job. After it expires, the key is forgotten: the same key with the same body starts a new job, and any prior conflict clears.

## Example

This submit includes an `Idempotency-Key`. If the call fails partway, you can run the exact same request again and get the same job back rather than a duplicate.

<CodeGroup>
  ```bash curl theme={null}
  curl https://www.wibbleai.com/api/v1/humanize \
    -H "Authorization: Bearer wib_live_..." \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: 3f9a1b6c-2d4e-4a7f-8c10-9e2b5d7a1f00" \
    -d '{"text": "The mitochondria is the powerhouse of the cell."}'
  ```

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

  import requests

  idempotency_key = str(uuid.uuid4())

  response = requests.post(
      "https://www.wibbleai.com/api/v1/humanize",
      headers={
          "Authorization": "Bearer wib_live_...",
          "Idempotency-Key": idempotency_key,
      },
      json={"text": "The mitochondria is the powerhouse of the cell."},
  )
  job = response.json()
  print(job["id"], job["status"])
  ```

  ```javascript JavaScript theme={null}
  import { randomUUID } from "node:crypto";

  const idempotencyKey = randomUUID();

  const response = await fetch("https://www.wibbleai.com/api/v1/humanize", {
    method: "POST",
    headers: {
      Authorization: "Bearer wib_live_...",
      "Content-Type": "application/json",
      "Idempotency-Key": idempotencyKey,
    },
    body: JSON.stringify({
      text: "The mitochondria is the powerhouse of the cell.",
    }),
  });
  const job = await response.json();
  console.log(job.id, job.status);
  ```
</CodeGroup>

The first request returns HTTP `202` and creates a job:

```json theme={null}
{
  "id": "7b42f1d6-0a8c-4e8f-9a21-c6d9f47c2b10",
  "status": "queued",
  "mode": "humanize",
  "input_words": 9,
  "words_reserved": 9,
  "words_charged": 9,
  "status_url": "https://www.wibbleai.com/api/v1/humanize/7b42f1d6-0a8c-4e8f-9a21-c6d9f47c2b10",
  "current_stage": "detecting_language",
  "detected_language": null,
  "created_at": "2026-06-14T12:00:00.000Z",
  "completed_at": null
}
```

Run the same call again with the same key and body, and Wibble returns the same `7b42f1d6-0a8c-4e8f-9a21-c6d9f47c2b10` with HTTP `200`. No second job is created, and no additional words are reserved.

<Tip>
  Set the idempotency key before you make the request, not after a failure. If the first attempt never returns, you still hold the key needed to retry safely.
</Tip>

## Related

<Columns cols={2}>
  <Card title="Submit a job" icon="paper-plane" href="/api-reference/humanize/submit">
    Every request option for `POST /humanize`, including the `Idempotency-Key` header.
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/concepts/errors">
    The `idempotency_conflict` response and the full error reference.
  </Card>
</Columns>
