# Idempotency

> Make Ur Next Door API retries safe with the Idempotency-Key header: key format, 24 hour replay, conflicts, and code to generate keys in four languages.

Source: https://docs.urnextdoor.com/idempotency/

If a request times out, you cannot know whether it worked. Sending it again might create a second post. The `Idempotency-Key` header solves this: send a unique key with each new request, and reuse it only when you retry that same request. The API then acts once, and answers every retry with the result of the first attempt.

## How it works

1. You send `POST /api/v1/posts` with `Idempotency-Key: 6f1c2b8e-...`.
2. The post is created and the API stores the `2xx` answer for **24 hours**, linked to that key.
3. You send the identical request again with the same key, within 24 hours. Nothing new is created. You get the stored answer, with the header `Idempotent-Replayed: true`.

Only successful (`2xx`) answers are stored. If the first attempt failed (a `422`, a `500`, anything not `2xx`), a retry with the same key runs the request again, so you can fix a problem and resend with the same key.

## The rules

| Rule | Detail |
| --- | --- |
| Where | Any `POST` or `DELETE`. Ignored on `GET`. |
| Format | 1 to 64 characters: letters, digits, `.`, `_`, `:` or `-`. A UUID is ideal. Anything else answers `400 invalid_idempotency_key`. |
| Lifetime | A stored answer replays for 24 hours. |
| Scope | Keys are remembered per API key. |
| Same key, different request | `422 idempotency_key_reused`. A key belongs to one exact request: the same method, path and body. |
| Same key while the first is running | `409 idempotency_in_progress`. Wait a moment and retry with the same key. |

## Generate a key

**cURL**

```bash
IDEMPOTENCY_KEY=$(uuidgen)
```

**JavaScript**

```js
const idempotencyKey = crypto.randomUUID();
```

**Python**

```python
import uuid

idempotency_key = str(uuid.uuid4())
```

**PHP**

```php
<?php
$idempotencyKey = bin2hex(random_bytes(16));
```

## Retry the right way

Create the key **once per item**, before the first attempt, and keep it for every retry of that item:

```python
import os
import time
import uuid

import requests

api_key = os.environ["UND_API_KEY"]
payload = {"title": "Five easy ways to save water at home", "body": "<p>Small habits add up.</p>"}
key = str(uuid.uuid4())  # one key for this post, reused on every retry

for attempt in range(3):
    try:
        r = requests.post(
            "https://urnextdoor.com/api/v1/posts",
            headers={"Authorization": f"Bearer {api_key}", "Idempotency-Key": key},
            json=payload,
            timeout=30,
        )
    except requests.RequestException:
        time.sleep(2 ** attempt)
        continue
    if r.status_code >= 500 or r.status_code == 409:
        time.sleep(2 ** attempt)
        continue
    print(r.status_code, r.json())
    break
```

When you create many items in a loop, make a new key for each item. Reusing one key for different posts answers `422 idempotency_key_reused`.

## Good keys from your own data

If your items already have a stable id (a spreadsheet row id, a database id), you can build the key from it, for example `sheet-row-1042`. Then even a workflow that runs twice by mistake within 24 hours cannot publish that row twice.

## Next steps

- [Errors](https://docs.urnextdoor.com/errors.md), including which ones to retry.
- [Rate limits and daily quotas](https://docs.urnextdoor.com/limits.md).
- [cURL](https://docs.urnextdoor.com/integrations/curl.md).
