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

# Deduplication

> Configure how webhook.co collapses duplicate inbound requests into a single event.

Webhook senders are **at-least-once**: on a network blip or a non-2xx response they retry, so the same
event can arrive more than once. webhook.co deduplicates inbound requests per endpoint so a retry collapses
into the event you already captured, instead of showing up (and metering) twice.

<Note>
  Deduplication is best-effort **load reduction**, not an exactly-once guarantee. Keep your own
  handler idempotent — dedup reduces duplicates, it doesn't promise their absence.
</Note>

## The default

Every endpoint starts on the **`identifier`** mode with a **24-hour** window. It keys each request on the
first stable signal it finds:

1. a Standard Webhooks `webhook-id` header,
2. a recognized provider's event id (header- or body-borne), then
3. a content hash of the request — the HTTP method, a canonicalized path + query, and the body, within
   the time window.

Two identical retries collapse; two genuinely different requests stay distinct. This default needs no
configuration.

## Modes

Set a per-endpoint `dedupConfig` to change how the key is derived:

| Mode                   | Keys on                                                      | Use it when                                                          |
| ---------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------- |
| `identifier` (default) | id ladder, then content hash                                 | the sender supplies a stable id, or you want the safe default        |
| `content`              | the canonical request content (method + path + query + body) | the sender's ids are unreliable or reused across distinct events     |
| `fields`               | operator-selected field paths                                | the sender has no stable id but a stable field (e.g. `body.data.id`) |
| `off`                  | nothing — every request is its own event                     | you want to capture every request (testing, debugging)               |

Each mode (except `off`) takes a `windowSeconds` (60–604800). `off` disables collapsing entirely.

<Warning>
  `off` means **every** request is a distinct, billable event — including retries and any
  non-webhook traffic (uptime checks, link previews) that reaches the URL. Use it deliberately.
</Warning>

## Field paths

`fields` mode keys on values you select with field paths. A path starts with a root — `headers`, `body`,
`query`, or `path` — and addresses into it with dot notation and array accessors:

* `body.data.id` — a nested body value
* `headers.x-event-id` — a header value
* `query.id` — a query parameter
* `body.items[*].sku` — every element of an array (`[*]` wildcard, or `[0]` for one index)

Provide `include` paths (the values that make up the key) and optionally `exclude` paths (volatile fields
to drop, like a per-delivery timestamp). If a configured field is missing from a request, that request is
treated as distinct rather than collapsed — webhook.co never silently drops an event it can't key.

## Configure it

Deduplication is configured identically across every surface — set it when you create an endpoint, or
update it any time. `--dedup-reset` (CLI) or a `null` `dedupConfig` (API) returns an endpoint to the
default.

From the CLI:

```sh theme={null}
wbhk endpoints create orders-prod --dedup-mode content --dedup-window 300
wbhk endpoints update <endpoint-id> --dedup-mode fields --dedup-field body.data.id
wbhk endpoints update <endpoint-id> --dedup-mode off
wbhk endpoints update <endpoint-id> --dedup-reset
```

From the API — set the config on create, or `PATCH` it later:

```sh theme={null}
curl -X POST https://api.webhook.co/v1/endpoints \
  -H "authorization: Bearer $WBHK_API_KEY" -H "content-type: application/json" \
  -d '{"name":"orders-prod","dedupConfig":{"mode":"content","windowSeconds":300}}'

curl -X PATCH https://api.webhook.co/v1/endpoints/<endpoint-id> \
  -H "authorization: Bearer $WBHK_API_KEY" -H "content-type: application/json" \
  -d '{"dedupConfig":{"mode":"fields","windowSeconds":300,"fields":{"include":["body.data.id"]}}}'
```

From a typed SDK:

```ts theme={null}
await webhook.endpoints.update({
  endpointId,
  dedupConfig: { mode: "content", windowSeconds: 300 },
});
```

Config changes take effect within the cache-propagation window (about five minutes), not instantly — and
a change never claims exactly-once across the transition. Every change is recorded in your endpoint's
tamper-evident audit trail.
