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

# Idempotency

> Stop duplicate events on the way in, and handle them on the way out.

## On the way in

Send the `X-Idempotency-Key` header with each event. hooksnode stores the first event with a key. When the same key comes again to the same project, hooksnode stores nothing and answers:

```json theme={null}
{ "status": "duplicate — already accepted" }
```

The status is `200`, so your sender does not retry.

```bash theme={null}
curl -X POST https://hooksnode.com/p/<source-id> \
  -H "X-Idempotency-Key: order_9311_paid" \
  -d '{"event":"order.paid","order_id":"9311"}'
```

* Keys are unique for each project.
* A key can be up to 255 characters.
* Use an ID that your system already has, such as the provider's event ID.

hooksnode sets keys for you on other sources:

| Source            | Key                                                    |
| ----------------- | ------------------------------------------------------ |
| WhatsApp          | A hash of the body, so Meta's retries are stored once. |
| Gmail             | `gmail:<source>:<message>`                             |
| Outlook           | `outlook:<source>:<message>`                           |
| IMAP, Watch a URL | `poll:<source>:…`                                      |
| Bank alerts       | The email's key plus `:bank`                           |

## On the way out

hooksnode delivers at least once. Your endpoint can get the same event twice. Make your handler safe to run twice:

1. Find a stable ID in the event. For example, Paystack's `data.reference`, the email's `message.id`, or the bank event's `email.id`.
2. Before you do the work, check if you already did it for that ID.
3. Store the ID in the same database transaction as the work.

```sql theme={null}
INSERT INTO processed_events (event_id) VALUES ($1)
ON CONFLICT (event_id) DO NOTHING
RETURNING event_id;
-- No row back: you already handled it. Answer 200 and stop.
```
