> ## Documentation index
> Fetch the complete documentation index at: https://dash.robopost.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency

A request can time out after Robopost has created the posts. If you simply send it again, you get the
posts twice. An `Idempotency-Key` header makes the retry safe: Robopost recognizes it and answers with the
result of the first request.

**You need:** a unique key for each post you intend to create, for example your own record ID.

## Send a key with every create

```bash
curl -X POST https://public-api.robopost.app/v1/scheduled_posts \
  -H "Authorization: Bearer $ROBOPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: campaign-42-post-7" \
  -d '{"text": "Hello", "channel_ids": ["<id>"], "schedule_at": "2026-11-20T15:00:00Z"}'
```

- **Same key, same body:** you get the first response again, with the header `Idempotent-Replayed: true`.
  No new posts are created.
- **Same key, different body:** `422 IDEMPOTENCY_KEY_REUSED`. Use a new key for a different post.
- **Same key while the first request is still running:** `409 IDEMPOTENCY_REQUEST_IN_PROGRESS` with
  `retry_after_seconds`. Wait and retry.
- If the first request failed with a server error, retrying with the same key runs it again. If it had
  already created some of the per-channel posts, the retry creates only the missing ones.

Keys are 1–255 printable characters without spaces, are scoped to your team, and are remembered for 7 days.

## Find what a request created

If you never got the response, ask for the posts created under the key:

```bash
curl "https://public-api.robopost.app/v1/scheduled_posts?idempotency_key=campaign-42-post-7" \
  -H "Authorization: Bearer $ROBOPOST_API_KEY"
```

## A retry loop that can't duplicate

```python
import time, uuid, requests

def create_post(session, body, key=None, attempts=5):
    key = key or str(uuid.uuid4())
    for attempt in range(attempts):
        try:
            r = session.post("https://public-api.robopost.app/v1/scheduled_posts",
                             json=body, headers={"Idempotency-Key": key}, timeout=30)
        except requests.RequestException:
            time.sleep(2 ** attempt)
            continue
        if r.status_code == 409 and r.json().get("code") == "IDEMPOTENCY_REQUEST_IN_PROGRESS":
            time.sleep(r.json().get("retry_after_seconds", 2))
            continue
        if r.status_code >= 500:
            time.sleep(2 ** attempt)
            continue
        return r
    raise RuntimeError(f"gave up; check GET /v1/scheduled_posts?idempotency_key={key}")
```

## Next

[Changes and webhooks](https://dash.robopost.app/docs/api/changes-and-webhooks.md)
