---
title: "Events"
description: "Every operation in this group: what it accepts, what it returns and the errors it can answer with."
url: "https://openemail.uk/docs/api/reference/events"
area: "API"
category: "Reference"
---

# Events

Every operation in this group: what it accepts, what it returns and the errors it can answer with.

## Operations

Things your contacts did, sent by your own code: an order placed, a trial started, a plan changed. An event is stored against the contact for 90 days. It starts every live automation whose trigger names it, ends a wait step that was waiting for it, and can be read by a branch.

Send one with `POST /events` or up to 100 with `POST /events/batch`. Both take an `Idempotency-Key` header, so a retry never records an event twice. A key or an app may send 600 events a minute. Sending needs `contacts:write` and reading the events of a contact needs `contacts:read`.

### `POST /events`

Send a contact event

Records that something happened to one contact, named by `email` or `contactId`. Every live automation whose trigger is this event name, and whose filters match `properties`, takes the contact in, and any wait step holding out for the name moves on. `enrolled` and `resumed` say what it set off.

An address nobody has saved is a 404 unless `createContact` is true, which adds it to the contacts first. An event sent with a test key is stored with `mode` `test` and starts nothing. A key or an app may send 600 events a minute.

Requires the `contacts:write` scope.

- Scopes: `contacts:write`.
- Honours `Idempotency-Key`.

**Headers**

- `Idempotency-Key` (`string`, up to 255 characters): Makes a retry safe. The same key with the same event returns the stored event with `replayed` true and starts nothing again. The same key with a different event is a 422 `idempotency_key_reuse`.

**Request body**

- `name` (`string`, required, 1 to 100 characters): What happened, such as `order.placed`: 1 to 100 letters, digits, dots, colons, dashes and underscores, starting with a letter or a digit. Names are matched exactly, case included.
- `email` (`string`, up to 320 characters, format `email`): The contact the event is about, by email address. Send this or `contactId`.
- `contactId` (`string`, 1 to 64 characters): The contact the event is about, by id. Send this or `email`.
- `properties` (`Record<string, any>`): Anything worth keeping about the event, as a JSON object of at most 50 keys and 4 KB. An event trigger can filter on these, and steps can put them in an email or a contact field.
- `occurredAt` (`string`, format `date-time`): When it happened, as an ISO 8601 instant. Left out, it is now. It cannot be in the future or more than 90 days ago.
- `createContact` (`boolean`): Add the address to the contacts when nobody has it yet. It needs `email`. Left out or false, an unknown address is a 404 `contact_not_found`.
- `contactName` (`string`, up to 200 characters): The name to give a contact that `createContact` adds.

**Returns**

- `200` `ContactEventResult`: A replay: the event this `Idempotency-Key` stored before.
- `201` `ContactEventResult`: Recorded.

**Errors**

- `404`: `contact_not_found` when nobody has that address or id and `createContact` is not true.
- `422`: `invalid_event` when the name, the properties or `occurredAt` break a rule or no contact is named, `invalid_parameter` or `unknown_parameter` for a body of the wrong shape, or `idempotency_key_reuse` when the key was already used for a different event.
- `429`: `event_rate_limited`: more events in a minute than the key or app may send.
- The errors every operation can return: `400`, `401`, `403`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`events.send()`](https://openemail.uk/docs/sdk/reference/events#send); Python [`events.send()`](https://openemail.uk/docs/python/reference/events#send); Ruby [`events.send`](https://openemail.uk/docs/ruby/reference/events#send); PHP [`events->send`](https://openemail.uk/docs/php/reference/events#send); Go [`Events.Send`](https://openemail.uk/docs/go/reference/events#send); Java [`events().send`](https://openemail.uk/docs/java/reference/events#send); C# [`Events.SendAsync`](https://openemail.uk/docs/csharp/reference/events#send); CLI [`openemail events send`](https://openemail.uk/docs/cli/reference/events#events-send).

### `POST /events/batch`

Send contact events in a batch

Records up to 100 events in one call, each handled exactly as `POST /events` handles one, in order. One event failing does not stop the rest: `accepted` holds the ones that were stored and `failed` the ones that were not, each with the `index` it had in the request. A body of the wrong shape is refused whole. The batch counts as that many events toward the 600 a minute a key or an app may send.

Requires the `contacts:write` scope.

- Scopes: `contacts:write`.
- Honours `Idempotency-Key`.

**Headers**

- `Idempotency-Key` (`string`, up to 255 characters): Makes a retry of the whole batch safe. Each event is remembered under the key and its position, so resend the same events in the same order.

**Request body**

- `events` (`object[]`, required, 1 to 100 items): The events, 1 to 100 of them, each in the shape a single event takes.
  - `name` (`string`, required, 1 to 100 characters): What happened, such as `order.placed`: 1 to 100 letters, digits, dots, colons, dashes and underscores, starting with a letter or a digit. Names are matched exactly, case included.
  - `email` (`string`, up to 320 characters, format `email`): The contact the event is about, by email address. Send this or `contactId`.
  - `contactId` (`string`, 1 to 64 characters): The contact the event is about, by id. Send this or `email`.
  - `properties` (`Record<string, any>`): Anything worth keeping about the event, as a JSON object of at most 50 keys and 4 KB. An event trigger can filter on these, and steps can put them in an email or a contact field.
  - `occurredAt` (`string`, format `date-time`): When it happened, as an ISO 8601 instant. Left out, it is now. It cannot be in the future or more than 90 days ago.
  - `createContact` (`boolean`): Add the address to the contacts when nobody has it yet. It needs `email`. Left out or false, an unknown address is a 404 `contact_not_found`.
  - `contactName` (`string`, up to 200 characters): The name to give a contact that `createContact` adds.

**Returns**

- `200` `ContactEventBatch`: What was stored and what was not.

**Errors**

- `422`: `invalid_parameter` or `unknown_parameter` for a body of the wrong shape, with `param` naming the event and the field, such as `events.3.name`.
- `429`: `event_rate_limited`: the batch would pass the events a minute the key or app may send. Nothing was stored.
- The errors every operation can return: `400`, `401`, `403`, `404`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`events.sendBatch()`](https://openemail.uk/docs/sdk/reference/events#sendBatch); Python [`events.send_batch()`](https://openemail.uk/docs/python/reference/events#sendBatch); Ruby [`events.send_batch`](https://openemail.uk/docs/ruby/reference/events#sendBatch); PHP [`events->sendBatch`](https://openemail.uk/docs/php/reference/events#sendBatch); Go [`Events.SendBatch`](https://openemail.uk/docs/go/reference/events#sendBatch); Java [`events().sendBatch`](https://openemail.uk/docs/java/reference/events#sendBatch); C# [`Events.SendBatchAsync`](https://openemail.uk/docs/csharp/reference/events#sendBatch); CLI [`openemail events send-batch`](https://openemail.uk/docs/cli/reference/events#events-send-batch).

### `GET /events/names`

List the event names in use

The distinct names of the events the workspace holds, in byte order, at most 100. It is what the app suggests when somebody picks the event that starts an automation. Events are kept for 90 days, so a name nothing has sent in that time is gone.

Requires the `automations:read` scope.

- Scopes: `automations:read`.

**Returns**

- `200` `ContactEventNameList`: The names.

**Errors**

- The errors every operation can return: `400`, `401`, `403`, `404`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`events.listNames()`](https://openemail.uk/docs/sdk/reference/events#listNames); Python [`events.list_names()`](https://openemail.uk/docs/python/reference/events#listNames); Ruby [`events.list_names`](https://openemail.uk/docs/ruby/reference/events#listNames); PHP [`events->listNames`](https://openemail.uk/docs/php/reference/events#listNames); Go [`Events.ListNames`](https://openemail.uk/docs/go/reference/events#listNames); Java [`events().listNames`](https://openemail.uk/docs/java/reference/events#listNames); C# [`Events.ListNamesAsync`](https://openemail.uk/docs/csharp/reference/events#listNames); CLI [`openemail events list-names`](https://openemail.uk/docs/cli/reference/events#events-list-names); MCP [`listContactEventNames`](https://openemail.uk/docs/mcp/tools/automations#listContactEventNames).

### `GET /contacts/{email}/events`

List the events of a contact

The events recorded for one contact in the last 90 days, the most recent first by when they happened. An app a member connected reads only the contacts that member added.

Requires the `contacts:read` scope.

- Scopes: `contacts:read`.

**Path parameters**

- `email` (`string`, required): The contact, by email address, compared without case. The `contactId` an event or an enrollment carries works here too.

**Query parameters**

- `name` (`string`, 1 to 100 characters): Only events with exactly this name.
- `limit` (`integer`, at least 1, at most 200, default `50`): Rows per page, 1 to 200.
- `cursor` (`string`): The id of the last event on the previous page. Keyset, not offset: pass the previous page's `nextCursor`. One that names nothing in this list is a 400 `invalid_cursor`.

**Returns**

- `200` `ContactEventList`: A page of events.

**Errors**

- `404`: `contact_not_found` when nobody you can reach has that address or id.
- The errors every operation can return: `400`, `401`, `403`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`contacts.listEvents()`](https://openemail.uk/docs/sdk/reference/contacts#listEvents), [`contacts.listAllEvents()`](https://openemail.uk/docs/sdk/reference/contacts#listAllEvents), [`contacts.iterateEvents()`](https://openemail.uk/docs/sdk/reference/contacts#iterateEvents); Python [`contacts.list_events()`](https://openemail.uk/docs/python/reference/contacts#listEvents), [`contacts.list_all_events()`](https://openemail.uk/docs/python/reference/contacts#listAllEvents), [`contacts.iterate_events()`](https://openemail.uk/docs/python/reference/contacts#iterateEvents); Ruby [`contacts.list_events`](https://openemail.uk/docs/ruby/reference/contacts#listEvents), [`contacts.list_all_events`](https://openemail.uk/docs/ruby/reference/contacts#listAllEvents), [`contacts.iterate_events`](https://openemail.uk/docs/ruby/reference/contacts#iterateEvents); PHP [`contacts->listEvents`](https://openemail.uk/docs/php/reference/contacts#listEvents), [`contacts->listAllEvents`](https://openemail.uk/docs/php/reference/contacts#listAllEvents), [`contacts->iterateEvents`](https://openemail.uk/docs/php/reference/contacts#iterateEvents); Go [`Contacts.ListEvents`](https://openemail.uk/docs/go/reference/contacts#listEvents), [`Contacts.ListAllEvents`](https://openemail.uk/docs/go/reference/contacts#listAllEvents), [`Contacts.IterateEvents`](https://openemail.uk/docs/go/reference/contacts#iterateEvents); Java [`contacts().listEvents`](https://openemail.uk/docs/java/reference/contacts#listEvents), [`contacts().listAllEvents`](https://openemail.uk/docs/java/reference/contacts#listAllEvents), [`contacts().iterateEvents`](https://openemail.uk/docs/java/reference/contacts#iterateEvents); C# [`Contacts.ListEventsAsync`](https://openemail.uk/docs/csharp/reference/contacts#listEvents), [`Contacts.ListAllEventsAsync`](https://openemail.uk/docs/csharp/reference/contacts#listAllEvents), [`Contacts.IterateEventsAsync`](https://openemail.uk/docs/csharp/reference/contacts#iterateEvents); CLI [`openemail contacts list-events`](https://openemail.uk/docs/cli/reference/contacts#contacts-list-events); MCP [`listContactEvents`](https://openemail.uk/docs/mcp/tools/automations#listContactEvents).

### Objects

#### `ContactEvent`

`object`

- `object` (`string`, required, one of `"contact_event"`)
- `id` (`string`, required): The id of the event.
- `contactId` (`string`, required): The id of the contact it is about.
- `email` (`string`, required, format `email`): The address of that contact.
- `name` (`string`, required): What happened, as it was sent.
- `properties` (`object`, required): The properties the event was sent with. Empty when it had none.
- `occurredAt` (`string`, required, format `date-time`): When it happened.
- `mode` (`string`, required, one of `"live"`, `"test"`): `test` when a test key sent it. A test event is stored and starts or resumes nothing.
- `createdAt` (`string`, required, format `date-time`): When OpenEmail received it.

#### `ContactEventBatch`

`object`

- `object` (`string`, required, one of `"contact_event_batch"`)
- `accepted` (`object[]`, required): The events that were stored, in the order they were sent.
  - `object` (`string`, required, one of `"contact_event"`)
  - `id` (`string`, required): The id of the event.
  - `contactId` (`string`, required): The id of the contact it is about.
  - `email` (`string`, required, format `email`): The address of that contact.
  - `name` (`string`, required): What happened, as it was sent.
  - `properties` (`object`, required): The properties the event was sent with. Empty when it had none.
  - `occurredAt` (`string`, required, format `date-time`): When it happened.
  - `mode` (`string`, required, one of `"live"`, `"test"`): `test` when a test key sent it. A test event is stored and starts or resumes nothing.
  - `createdAt` (`string`, required, format `date-time`): When OpenEmail received it.
  - `replayed` (`boolean`, required): True when this `Idempotency-Key` was used before and the stored event is returned, with nothing started again.
  - `enrolled` (`string[]`, required): The ids of the automations the event put the contact into.
  - `resumed` (`integer`, required, at least 0): How many waits for this event it ended, across every automation the contact is in.
  - `index` (`integer`, required, at least 0): Where the event sat in the request, from 0.
- `failed` (`object[]`, required): The events that were not stored.
  - `index` (`integer`, required, at least 0): Where the event sat in the request, from 0.
  - `code` (`string`, required): Why, as the single call would answer: `invalid_event`, `contact_not_found` or `idempotency_key_reuse`.
  - `message` (`string`, required): The same in a sentence.

#### `ContactEventList`

`object`

- `object` (`string`, one of `"list"`)
- `data` (`ContactEvent[]`)
- `hasMore` (`boolean`): True when another page follows. Pass `nextCursor` back as `cursor` to read it.
- `nextCursor` (`string`, nullable): The id of the last row on this page, or null on the last page.

#### `ContactEventName`

`object`

- `object` (`string`, required, one of `"contact_event_name"`)
- `name` (`string`, required): An event name the workspace holds events for.

#### `ContactEventNameList`

`object`

- `object` (`string`, one of `"list"`)
- `data` (`ContactEventName[]`)

#### `ContactEventResult`

`object`

- `object` (`string`, required, one of `"contact_event"`)
- `id` (`string`, required): The id of the event.
- `contactId` (`string`, required): The id of the contact it is about.
- `email` (`string`, required, format `email`): The address of that contact.
- `name` (`string`, required): What happened, as it was sent.
- `properties` (`object`, required): The properties the event was sent with. Empty when it had none.
- `occurredAt` (`string`, required, format `date-time`): When it happened.
- `mode` (`string`, required, one of `"live"`, `"test"`): `test` when a test key sent it. A test event is stored and starts or resumes nothing.
- `createdAt` (`string`, required, format `date-time`): When OpenEmail received it.
- `replayed` (`boolean`, required): True when this `Idempotency-Key` was used before and the stored event is returned, with nothing started again.
- `enrolled` (`string[]`, required): The ids of the automations the event put the contact into.
- `resumed` (`integer`, required, at least 0): How many waits for this event it ended, across every automation the contact is in.
