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

# Webhook retries and deduplication

> Make webhook processing safe to repeat.

Return a successful response after durable enqueue or storage. If your
endpoint is unavailable, Skinloop may retry. Use the stable event `id` as a
unique key and make each status transition idempotent. A later event may
arrive before an earlier retry, so compare event timestamps and current
state rather than assuming delivery order.

```sql theme={null}
-- Conceptual receiver constraint, adapted to your datastore
create unique index webhook_events_event_id on webhook_events (event_id);
```

An identical delivery should return 2xx without fulfilling again. The same
event ID with different signed bytes is a conflict and should not be processed.
Keep the event ID, payment ID, merchant order ID, status, and raw payload
digest for reconciliation. If webhook delivery is delayed, poll the checkout
status endpoint as a recovery path.

Webhook deduplication and fulfillment deduplication are separate:

* Use the webhook `id` to store each signed event once.
* Use a stable fulfillment key derived from the merchant order and checkout to
  create one durable fulfillment job.
* Have both the webhook handler and status poller insert that same job with a
  unique constraint.
* Return 2xx after the authenticated event and job are committed. Do not wait
  for external fulfillment inside the webhook request.

See [Safe fulfillment](/fulfillment-safety) for the complete outbox, worker,
idempotency, and crash-recovery pattern.
