---
title: "Poll the event feed"
method: GET
path: "/events"
tags: ["Events"]
---

# Poll the event feed

`GET /events`

## Request

- Base URL: `https://api.layout.link/v1`
- URL: `https://api.layout.link/v1/events`
- Auth: HTTP bearer

The same events your webhooks receive, oldest first. Read-only: walking it acknowledges nothing. Filters combine.

## Query parameters

- `cursor` string
- `after` string
- `since` string, date-time
- `order_id` string
- `user_id` string
- `event` 'order.building' | 'order.carted' | 'order.placing' | 'order.placed' | 'order.failed' | 'order.unconfirmed' | 'order.needs_approval' | 'order.challenge' | 'order.updated'
- `limit` integer

## Headers

- `Layout-Version` '2026-10-01'

## Response `200`

A page of events.

- EventsPage
  - `events` Event[], required
    - `event` 'order.building' | 'order.carted' | 'order.placing' | 'order.placed' | 'order.failed' | 'order.unconfirmed' | 'order.needs_approval' | 'order.challenge' | 'order.updated', required: `order.updated` comes after `order.placed` each time more pickup details arrive (the name the order is under, the ready time, the store's own order number, instructions). Layout reads them from several places, such as the store's confirmation screen and its email, so they can arrive in more than one wave, minutes apart. On a delivery order it also comes when the driver picks the food up, when it is delivered, and when delivery could not happen (the order then reads as a pickup with `changed_from: "delivery"`). Handle every one. Its `state` stays `placed`. Read GET /orders/{orderId} for the details.
    - `event_id` string, required
    - `delivery_id` string, required: This delivery. A retry of it keeps the id; a replay gets a new one.
    - `test` true: A test delivery: the console's Send test event, POST /v1/sandbox/webhooks/fire, or `layout trigger`.
    - `order` object, required: No item names, modifiers or pickup details: read GET /orders/{orderId} for the full order.
      - `id` string, required
      - `state` 'building' | 'carted' | 'placing' | 'placed' | 'failed' | 'unconfirmed' | 'refunded', required
      - `store` string, nullable, required: The store's name.
      - `store_address` string, nullable: The store's address, when Layout knows it. Null when `store` is null. Absent only on events written before this field existed.
      - `items` integer, required: How many items the order holds. The items themselves are not in the payload.
      - `total_minor` integer, nullable, required: Minor units. 2140 is $21.40. Null before the cart is priced.
      - `currency` string, required
      - `simulated` true: Present and true on a sandbox order that was simulated. Absent on every real order.
      - `failure_code` string, nullable: On `order.failed` only: the same stable machine code `GET /orders/{orderId}` returns, or null when Layout recorded none.
    - `user` object, required
      - `id` string, required
    - `occurred_at` string, required: When the order changed. Use it, not `sent_at`, to order events.
    - `sent_at` string, required: When Layout wrote this delivery.
  - `nextCursor` string, nullable, required: Null at the end. To resume later, pass the last `event_id` as `after`.

## Other responses

- `400`: `invalid_request`: a filter is malformed, `after` names no event in this feed, or both `cursor` and `after` were sent. `unsupported_version`: the Layout-Version header names no version this API answers.
- `401`: Secret not recognised, malformed, or rotated.
- `403`: Application not permitted. Usually production on an unapproved application.
- `429`: Too many requests.
- `500`: Layout failed to answer. Retry with backoff.
- `503`: Layout or a dependency could not be reached. Retry with backoff.

---

[API](https://skmtc.dev/layout/apis/layout-partner-api.md) · [All operations](https://skmtc.dev/layout/apis/layout-partner-api/llms.txt) · [OpenAPI document](https://skmtc.dev/layout/apis/layout-partner-api/revisions/7ff4ba01bf56?raw)
