---
title: "Idempotency"
url: https://docs.light-ai.app/idempotency.md
canonical_url: https://docs.light-ai.app/idempotency
last_updated: 2026-10-13
summary: "Send an Idempotency-Key with writes so retries never create duplicates or charge twice."
api_status: beta
related:
  - https://docs.light-ai.app/errors
  - https://docs.light-ai.app/mockups
  - https://docs.light-ai.app/external-refs
---

# Idempotency

> Send an Idempotency-Key with writes so retries never create duplicates or charge twice.

Add `Idempotency-Key` (8–128 visible ASCII characters; a UUID is ideal) to write requests (`POST` and `PATCH`; each reference page says whether it applies). The same key with the same body replays the first response for 24 hours, with `Idempotent-Replayed: true`.

curl:

```bash
curl -X POST "https://api.light-ai.app/v1/leads" \
  -H "Authorization: Bearer $LIGHT_AI_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "source": "api",
  "contact": {
    "first_name": "Dana",
    "email": "dana@example.com"
  }
}'
```

JavaScript:

```javascript
const response = await fetch('https://api.light-ai.app/v1/leads', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.LIGHT_AI_API_KEY}`,
    'Idempotency-Key': crypto.randomUUID(),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "source": "api",
    "contact": {
      "first_name": "Dana",
      "email": "dana@example.com"
    }
  }),
});
const data = await response.json();
```

Python:

```python
import os, uuid, requests

response = requests.post(
    "https://api.light-ai.app/v1/leads",
    headers={
        "Authorization": f"Bearer {os.environ['LIGHT_AI_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "source": "api",
        "contact": {
            "first_name": "Dana",
            "email": "dana@example.com"
        }
    },
)
data = response.json()
```

| Situation | Answer |
| --- | --- |
| Same key, same body, first request finished | The stored response, `Idempotent-Replayed: true` |
| Same key while the first is still running | Waits for the first to finish, then answers with its stored response (`Idempotent-Replayed: true`); if the first failed, the retry runs as a new request |
| Same key, different body | 422 `idempotency_key_reused` |

## Mockups

`POST /v1/projects/{id}/mockups` **requires** the header, and its replay is permanent (not only 24 hours): a retry with the same key returns the same generation and never charges again. For live keys it is scoped to the organization and your partner account (or the organization alone for organization keys), not to one API key, so a retry after rolling a key is still safe.

## Keys in automation tools

Use a value that is stable across retries of one logical request, ideally the source record id (`meta-{{leadgen_id}}`). In n8n without one, use `{{$execution.id}}-{{$runIndex}}-{{$itemIndex}}` so each item gets its own key. Idempotency keys are scoped to your API key and the organization (and live or test mode), so one partner workflow is safe across contractors.

> **Tip:** Leads have a second safety net: an `external_ref` your integration already sent replays the original lead (`replayed: true`) at any time, not only within 24 hours.
