# List events for a broadcast

`GET /v1/email/broadcasts/{broadcast_id}/events`

Returns the per-recipient delivery timeline for a broadcast, oldest first, as a cursor page. Each entry is one event, such as a send, an open, a click, or a bounce.

## Code samples

### TypeScript

```ts
for await (const event of bird.broadcasts.listEvents("eb_01krdgeqcxet5s7t44vh8rt9mg", {
  type: "email.bounced",
})) {
  console.log(event.type, event.recipient_id, event.bounce_type);
}
```

### Python

```py
for event in client.broadcasts.list_events(
    "eb_01krdgeqcxet5s7t44vh8rt9mg", type="email.bounced"
):
    print(event.type, event.recipient_id, event.bounce_type)
```

### Go

```go
for event, err := range client.Broadcasts.ListEvents(context.Background(),
	"eb_01krdgeqcxet5s7t44vh8rt9mg", bird.BroadcastsListEventsParams{Type: "email.bounced"}) {
	if err != nil {
		log.Fatal(err)
	}
	// bounce_type is absent unless the event is a bounce the server classified.
	if event.BounceType != nil {
		fmt.Println(event.Type, event.RecipientId, *event.BounceType)
	}
}
```

### PHP

```php
$events = $bird->broadcasts->listEvents('eb_01krdgeqcxet5s7t44vh8rt9mg', ['type' => 'email.bounced']);
foreach ($events as $event) {
    echo $event->getType(), ' ', $event->getRecipientId(), ' ', $event->getBounceType(), PHP_EOL;
}
```

### CLI

```sh
bird email broadcasts list-events <broadcast-id>
```

### cURL

```sh
curl -X GET "https://us1.platform.bird.com/v1/email/broadcasts/{broadcast_id}/events" \
  -H "Authorization: Bearer $TOKEN" \
  --url-query "limit=25"
```

## Example response `200`

```json
{
  "data": [
    {
      "id": "ev_01krdgeqcxet5s7t44vh8rt9mg",
      "type": "email.delivered",
      "recipient_id": "er_01krdgeqcxet5s7t44vh8rt9mg",
      "bounce_type": "hard",
      "bounce_code": "5.1.1",
      "rejection_reason": "recipient_suppressed",
      "link_name": "Faster exports, docs",
      "country": "US"
    }
  ],
  "next": [
    {
      "kind": "operation"
    }
  ],
  "next_cursor": "eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9",
  "prev_cursor": null,
  "refresh_cursor": "eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"
}
```

## Path parameters

- `broadcast_id` (string): ID of the broadcast whose event timeline to read.

## Query parameters

- `limit` (integer): Maximum number of items to return per page.
- `starting_after` (string): Cursor from the `next_cursor` field of a previous list response. Returns items immediately after the cursor position in the current sort order.
- `ending_before` (string): Cursor from the `prev_cursor` or `refresh_cursor` field of a previous list response. Returns items immediately before the cursor position in the current sort order. `prev_cursor` returns the preceding page. `refresh_cursor` anchors at the first row of that response, which on a newest-first sort is how to fetch the items that have appeared since.
- `type` (string)

  Filter by event type, for example `email.bounced` or `email.opened`. A broadcast timeline is recipient-scoped, so `email.scheduled` and `email.canceled` never appear on it; a canceled broadcast reports that in its own `status`.

  Possible values (may grow over time): `email.accepted`, `email.bounced`, `email.canceled`, `email.clicked`, `email.complained`, `email.deferred`, `email.delivered`, `email.list_unsubscribed`, `email.opened`, `email.out_of_band_bounce`, `email.processed`, `email.rejected`, `email.scheduled`, `email.unsubscribed`

## Response body

- `data` (array of object, required): Page of timeline events for this email send, in chronological order.
- `data.id` (string, required): Event ID.
- `data.type` (string, required)

  The event's type. `email.processed`, for example, means the message has been processed and queued for delivery.

  Possible values (may grow over time): `email.accepted`, `email.bounced`, `email.canceled`, `email.clicked`, `email.complained`, `email.deferred`, `email.delivered`, `email.list_unsubscribed`, `email.opened`, `email.out_of_band_bounce`, `email.processed`, `email.rejected`, `email.scheduled`, `email.unsubscribed`
- `data.occurred_at` (string, required): When this event occurred.
- `data.recipient_id` (string, required): Recipient this event applies to.
- `data.bounce_type` (nullable string)

  Bounce classification. Present on `email.bounced`, `email.out_of_band_bounce`, and
  `email.deferred` events.

  - `hard`: a permanent failure (invalid address or non-existent domain).
  - `soft`: a transient failure (mailbox full or server temporarily unavailable).
  - `block`: the receiving mail server blocked the sending IP for reputation reasons.
  - `admin`: an administrative refusal (relaying denied or blocklisted domain).
  - `undetermined`: the receiving server's response is ambiguous.

  Possible values: `hard`, `soft`, `undetermined`, `admin`, `block`, `null`
- `data.bounce_class` (nullable integer): A more detailed numeric bounce code, useful for telling apart failures that share the same `bounce_type`. For example, a DNS failure and a spam block can both come through as `bounce_type: soft` or `bounce_type: block`; this field tells you which one actually happened. Present on `email.bounced`, `email.out_of_band_bounce`, and `email.deferred` events.
- `data.bounce_code` (nullable string): SMTP status code returned by the receiving mail server. Present on `email.bounced` and `email.deferred` events.
- `data.bounce_description` (nullable string): The bounce reason, in plain language, as reported by the mail server. Present on `email.bounced` and `email.deferred` events.
- `data.rejection_reason` (nullable string)

  Specific cause of rejection. Present on `email.rejected` events only.

  - `recipient_suppressed`: The recipient is on the workspace suppression list.
  - `transmission_failed`: The message could not be transmitted for delivery.
  - `generation_failure`: The message could not be built for delivery, because of a template or content issue.
  - `policy_rejection`: The message was refused by sending policy.
  - `domain_unverified`: The sending domain was not verified.
  - `quota_exceeded`: The organization's send quota was reached.
  - `recipient_not_allowed`: This recipient was not allowed for this send. For a send from the shared onboarding domain, every recipient has to be a verified member of the workspace.

  Possible values: `recipient_suppressed`, `transmission_failed`, `generation_failure`, `policy_rejection`, `domain_unverified`, `quota_exceeded`, `recipient_not_allowed`, `null`
- `data.sending_ip` (nullable string): The IP address used to send this message. Useful for spotting a deliverability problem that is tied to one specific sending IP rather than affecting all of them. Present on `email.delivered`, `email.bounced`, `email.out_of_band_bounce`, and `email.deferred` events.
- `data.mailbox_provider` (nullable string): The recipient mailbox provider, as a lowercased classifier bucket (e.g. `gmail`, `yahoo`, `microsoft`, `apple`). Present on `email.processed`, `email.delivered`, `email.complained`, `email.bounced`, `email.out_of_band_bounce`, `email.deferred`, `email.rejected`, `email.unsubscribed`, `email.list_unsubscribed`, `email.opened`, and `email.clicked` events when the receiving mail system could be classified; null when it could not.
- `data.mailbox_provider_region` (nullable string): The provider region, as reported by the receiving mail system (for example `NA`, `EU`, `APAC`). The set is open and provider-specific. Present on `email.processed`, `email.delivered`, `email.complained`, `email.bounced`, `email.out_of_band_bounce`, `email.deferred`, `email.rejected`, `email.unsubscribed`, `email.list_unsubscribed`, `email.opened`, and `email.clicked` events when reported; null otherwise.
- `data.is_prefetched` (nullable boolean): True when the open was auto-fetched by an inbox privacy feature (Apple Mail Privacy Protection, the Gmail image proxy) rather than a person actually opening the message. Use it to calculate open rate accurately. Present on `email.opened` events only.
- `data.url` (nullable string): The clicked URL. Present on `email.clicked` events, and on `email.unsubscribed` events when the recipient unsubscribed through a link in the message.
- `data.link_name` (nullable string): The clicked link's own name, when the link in the message carried one, so a click can be reported by what the link said rather than where it pointed. Absent when the link had no name. Appears alongside `url` on `email.clicked` events, and on `email.unsubscribed` events when the recipient unsubscribed through a link.
- `data.country` (nullable string): ISO 3166-1 alpha-2 country code derived from the client IP. Present on `email.opened` and `email.clicked` events when available.
- `data.ip_address` (nullable string): Client IP address (IPv4 or IPv6). Present on `email.opened` and `email.clicked` events when available.
- `data.user_agent` (nullable string): Client user-agent string. Present on `email.opened` and `email.clicked` events when available.
- `next` (array of object)

  What to do next, given what this page reports. Present only where the read computes it: an
  empty list means the answer you were looking for is here and there is nothing further to
  do. Absent entirely on reads that do not report next actions.
- `next.kind` (string, required)

  What you do about this step.

  - `operation`: call the operation named in `operation`, then
    read again.
  - `external`: act somewhere this API does not reach, then read
    again.
  - `wait`: nothing is asked of you, so read again later.
  - `terminal`: nothing you do resolves this, so stop retrying.

  Tolerate a value you do not recognize: show the `description` and
  offer no action.

  Possible values (may grow over time): `operation`, `external`, `wait`, `terminal`
- `next.description` (string, required): A short, human-readable label for the step, suitable for display.
- `next.operation` (string): The operationId to call. Present only when `kind` is `operation`. The operation's own schema says how to call it; this says only which one, and what to address it with.
- `next.params` (object): The parameters that address the operation, by name: `{"sender_id": "…"}` for an operation on `/v1/sms/senders/{sender_id}/requirements`. A parameter the operation takes in its query string is given the same way, so an operation addressed as `?subject_id=` carries `{"subject_id": "…"}`. Every parameter the call needs is here, whether its value came from the thing you were acting on or is fixed for this step, so you can make the call from this object alone. Present only when `kind` is `operation` and the operation names a subject. A request body, when the operation takes one, is described by the operation's own schema and never appears here.
- `next.url` (string): A URL to open. Present only when `kind` is `external`, and only when the step has one. An external step whose `description` says to go and do something with no URL to open is normal.
- `next_cursor` (nullable string, required): Cursor for the next page. Pass back as `starting_after` to advance forward. `null` when no next page exists.
- `prev_cursor` (nullable string, required): Cursor for the previous page. Pass back as `ending_before` to step backward. `null` when no previous page exists.
- `refresh_cursor` (nullable string, required): Refresh anchor, the first row of this response. Pass back as `ending_before` to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-`null` whenever `data` is non-empty; `null` only on an empty page. Distinct from `prev_cursor`.

## Related resources

- [Getting started with email](/learn/email/getting-started-with-email) (video)
- [Email](/products/email) (product)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first email](/docs/get-started/send-your-first-email) (docs)

[Get an implementation brief](/learn/workspace?topic=email)
