# Events

## List events

**get** `/v1/events`

Lists your brand's events, oldest first.

**Behavior**

- An event is recorded when it HAPPENS, not when it is delivered. A brand
  with no webhook endpoint still accumulates events, which is what makes
  this readable after you register one.
- Ordering is a stable total order: the event's instant paired with its
  unique id. Paging twice returns the same rows in the same places.
- Scoped to your key's brand by construction: no parameter names a brand.

### Query Parameters

- `chat_id: optional string`

  Only events for this chat. Chat events only, since a batch run is not a chat.

- `cursor: optional string`

  Resume from a previous page's `next_cursor`.

- `limit: optional number`

  Page size; absent or outside 1–100 reads as 100.

- `since: optional string`

  Only events at or after this RFC3339 instant. A value that is not a timestamp is refused, never ignored.

- `type: optional string`

  Only this event type.

### Returns

- `data: array of Event`

  - `id: string`

    Stable id, `evt_…`. Unique for the life of the event.

  - `created_at: string`

    When the event happened. For a chat event this is the instant the chat recorded it.

  - `data: unknown`

    The event body, exactly what a webhook delivery for this event carries, so the same parser reads both.

  - `seq_from: number`

    First chat `seq` this event covers.

  - `seq_to: number`

    Last chat `seq` this event covers.

  - `type: string`

    What happened, e.g. `message.received`. The same names an endpoint subscribes to.

  - `batch_id: optional string`

    The invitation-batch run this event belongs to. Present on `invitation_batch.*` events only.

  - `chat_id: optional string`

    The chat this event belongs to, on chat events. A brand-scoped event
    (`brand.escalation_handling_changed`) carries its synthetic feed id here
    instead (`brand:<brand_id>`), which is load-bearing for ordering and
    dedup exactly as a chat id is.

- `next_cursor: string`

  Pass back verbatim to read the next page. Empty string when the log is exhausted.

### Example

```http
curl https://messages.api.linqapp.com/v1/events \
    -H "Authorization: Bearer $LINQ_AMB_API_KEY"
```

#### Response

```json
{
  "data": [
    {
      "id": "evt_9c2f4a1b",
      "type": "message.received",
      "created_at": "2026-08-14T05:12:44Z",
      "data": {
        "body": "where is my driver?"
      },
      "chat_id": "chat_4f81b2",
      "seq_from": 13,
      "seq_to": 13
    }
  ],
  "next_cursor": ""
}
```

## Get event

**get** `/v1/events/{evt}`

Reads one event.

**Behavior**

- An id belonging to another brand answers `404`, never `403`. The two are
  the same answer here, so this route cannot confirm an id exists elsewhere.

### Path Parameters

- `evt: string`

### Returns

- `Event object { id, created_at, data, 5 more }`

  One event in your brand's log.

  The attribution is a typed pair: a chat event carries `chat_id`,
  an `invitation_batch.*` event carries `batch_id`, and exactly one of the two
  is present on any event. They are never both set and never interchangeable.

  - `id: string`

    Stable id, `evt_…`. Unique for the life of the event.

  - `created_at: string`

    When the event happened. For a chat event this is the instant the chat recorded it.

  - `data: unknown`

    The event body, exactly what a webhook delivery for this event carries, so the same parser reads both.

  - `seq_from: number`

    First chat `seq` this event covers.

  - `seq_to: number`

    Last chat `seq` this event covers.

  - `type: string`

    What happened, e.g. `message.received`. The same names an endpoint subscribes to.

  - `batch_id: optional string`

    The invitation-batch run this event belongs to. Present on `invitation_batch.*` events only.

  - `chat_id: optional string`

    The chat this event belongs to, on chat events. A brand-scoped event
    (`brand.escalation_handling_changed`) carries its synthetic feed id here
    instead (`brand:<brand_id>`), which is load-bearing for ordering and
    dedup exactly as a chat id is.

### Example

```http
curl https://messages.api.linqapp.com/v1/events/$EVT \
    -H "Authorization: Bearer $LINQ_AMB_API_KEY"
```

#### Response

```json
{
  "id": "evt_9c2f4a1b",
  "type": "message.received",
  "created_at": "2026-08-14T05:12:44Z",
  "data": {
    "body": "where is my driver?"
  },
  "chat_id": "chat_4f81b2",
  "seq_from": 13,
  "seq_to": 13
}
```

## Domain Types

### Event

- `Event object { id, created_at, data, 5 more }`

  One event in your brand's log.

  The attribution is a typed pair: a chat event carries `chat_id`,
  an `invitation_batch.*` event carries `batch_id`, and exactly one of the two
  is present on any event. They are never both set and never interchangeable.

  - `id: string`

    Stable id, `evt_…`. Unique for the life of the event.

  - `created_at: string`

    When the event happened. For a chat event this is the instant the chat recorded it.

  - `data: unknown`

    The event body, exactly what a webhook delivery for this event carries, so the same parser reads both.

  - `seq_from: number`

    First chat `seq` this event covers.

  - `seq_to: number`

    Last chat `seq` this event covers.

  - `type: string`

    What happened, e.g. `message.received`. The same names an endpoint subscribes to.

  - `batch_id: optional string`

    The invitation-batch run this event belongs to. Present on `invitation_batch.*` events only.

  - `chat_id: optional string`

    The chat this event belongs to, on chat events. A brand-scoped event
    (`brand.escalation_handling_changed`) carries its synthetic feed id here
    instead (`brand:<brand_id>`), which is load-bearing for ordering and
    dedup exactly as a chat id is.

### Event List Response

- `EventListResponse object { data, next_cursor }`

  - `data: array of Event`

    - `id: string`

      Stable id, `evt_…`. Unique for the life of the event.

    - `created_at: string`

      When the event happened. For a chat event this is the instant the chat recorded it.

    - `data: unknown`

      The event body, exactly what a webhook delivery for this event carries, so the same parser reads both.

    - `seq_from: number`

      First chat `seq` this event covers.

    - `seq_to: number`

      Last chat `seq` this event covers.

    - `type: string`

      What happened, e.g. `message.received`. The same names an endpoint subscribes to.

    - `batch_id: optional string`

      The invitation-batch run this event belongs to. Present on `invitation_batch.*` events only.

    - `chat_id: optional string`

      The chat this event belongs to, on chat events. A brand-scoped event
      (`brand.escalation_handling_changed`) carries its synthetic feed id here
      instead (`brand:<brand_id>`), which is load-bearing for ordering and
      dedup exactly as a chat id is.

  - `next_cursor: string`

    Pass back verbatim to read the next page. Empty string when the log is exhausted.
