# Event ids on every webhook

October 1, 2026 · Webhooks

Delivery is at least once, so the same event can reach you more than once. Every delivery now carries an id for the event itself, which makes deduplicating a one-line check.

## What changed

- **`event_id`** names one event: this order reaching this state. Every delivery of it carries the same id, to every endpoint, on every retry and every replay.
- **`occurred_at`** is when the order changed. `sent_at` is still when Layout wrote the delivery.
- **`order.failure_code`** is on `order.failed`: the same stable code `GET /v1/orders/:id` returns, or `null` when Layout recorded none.
- **`GET /v1/events`** takes `after`, an `event_id` you already handled, and returns what came next. It also filters by `order_id`, `user_id`, `event` and `since`. `cursor` keeps working.

## Why it matters

Keep the last `event_id` you processed. After an outage, ask the feed for everything after it, and skip any `event_id` you have already seen.

## What to do

Deduplicate on `event_id` and order events by `occurred_at`. See [Payload](https://developer.layout.link/reference/webhooks#payload) and [GET /v1/events](https://developer.layout.link/reference/api#get-v1-events).

## Breaking changes

None. The new fields are additive. Events written before this change carry them too, in the feed, on a retry still queued, and in a replay from the console.
