---
title: "client.Events"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://openemail.uk/docs/go/reference/events"
area: "Go"
category: "Reference"
---

# client.Events

Every method in this namespace: its signature, its parameters, what it returns and an example.

## Methods

Things your contacts did, sent by your own code, such as an order placed or a trial started. An event starts the automations whose trigger names it and ends the waits that were holding out for it.

### `Events.Send`

Record that something happened to a contact

```go
Send(ctx context.Context, body openemail.Body, opts ...openemail.RequestOption) (openemail.Object, error)
```

Records an event against one contact, named by `email` or `contactId`: an order placed, a trial started, a plan changed. 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` in the answer say what it set off. Events are kept for 90 days.

Every call carries an `Idempotency-Key`. The SDK generates one per call and reuses it on that call's retries, so a retried network failure returns the stored event instead of recording a second one. Pass `openemail.WithIdempotencyKey` to extend that across processes and restarts. A replay returns `replayed: true` and starts nothing again. The same key with a different event is a 422 `idempotency_key_reuse`.

An event sent with a test key is stored with `mode` `test` and starts or resumes nothing.

Scopes: `contacts:write`.

**Parameters**

- `name` (`string`, required): 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`): The contact the event is about, by email address. Send this or `contactId`.
- `contactId` (`string`): The contact the event is about, by id. Send this or `email`.
- `properties` (`openemail.Body`): Anything worth keeping about the event, as a map of at most 50 keys and 4 KB of JSON. An event trigger can filter on these, and steps can put them in an email or a contact field.
- `occurredAt` (`time.Time | string`): When it happened, as a `time.Time` or an ISO 8601 string. Left out, it is now. It cannot be in the future or more than 90 days ago.
- `createContact` (`bool`): 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`): The name to give a contact that `createContact` adds, at most 200 characters.
- `openemail.WithIdempotencyKey` (`string`): Your own key, 1 to 255 characters of letters, digits, `_`, `.`, `:` or `-`. Anything else is a 400 `invalid_idempotency_key`.
- `openemail.WithAPIKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An `openemail.Object`: the event, with `id`, `contactId`, `email`, `name`, `properties`, `occurredAt`, `mode` and `createdAt`, plus `replayed`, `enrolled`, the ids of the automations it put the contact into, and `resumed`, how many waits it ended.

**Example**

```go
event, err := client.Events.Send(ctx, openemail.Body{
	"name":          "order.placed",
	"email":         "ada@example.com",
	"properties":    openemail.Body{"orderId": "AC-4192", "total": 129},
	"createContact": true,
}, openemail.WithIdempotencyKey("order:AC-4192:placed"))
if err != nil {
	return err
}

fmt.Println(event.String("id"), event.Strings("enrolled"), event.Int("resumed"), event.Bool("replayed"))
```

**Notes**

- A name, properties or an `occurredAt` that break a rule, or a body that names no contact, is 422 `invalid_event` with `param` naming the field.
- A key or an app may send 600 events a minute. Past that the call is 429 `event_rate_limited`.
- An app a member connected reaches only the contacts that member added, and a contact it creates belongs to that member.
- Retried automatically on network failure and retryable statuses, because the idempotency key makes a retry safe.

Also available in: API [`POST /events`](https://openemail.uk/docs/api/reference/events#post-events); 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); 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).

### `Events.SendBatch`

Record up to 100 contact events in one call

```go
SendBatch(ctx context.Context, events []openemail.Body, opts ...openemail.RequestOption) (openemail.Object, error)
```

Records each event in order, as if `Events.Send` had been called for it, and reports per event. It is never all or nothing: `accepted` holds the events that were stored and `failed` the ones that were not, each with the `index` it had in the slice, so one unknown address does not stop the rest. The call returns without an error whenever the batch was processed, so check `failed` instead of relying on the error.

The batch shares one `Idempotency-Key`, generated once per call or supplied as `openemail.WithIdempotencyKey`, and the server remembers each event under the key and its position. Retrying the same slice replays the events that were already stored and records only the ones that were not. Reordering the slice between attempts makes an event that moved fail with `idempotency_key_reuse`.

Scopes: `contacts:write`.

**Parameters**

- `events` (`[]openemail.Body`, required): The events, 1 to 100 of them, each in the shape `Events.Send` takes: `name`, `email` or `contactId`, and optionally `properties`, `occurredAt`, `createContact` and `contactName`.
- `openemail.WithIdempotencyKey` (`string`): Your own key for the whole batch, 1 to 255 characters of letters, digits, `_`, `.`, `:` or `-`.
- `openemail.WithAPIKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An `openemail.Object` with `accepted`, each a contact event result object plus its `index`, and `failed`, each with `index`, `code` and `message`.

**Example**

```go
batch, err := client.Events.SendBatch(ctx, []openemail.Body{
	{"name": "trial.started", "email": "ada@example.com"},
	{
		"name":       "trial.started",
		"email":      "grace@example.com",
		"properties": openemail.Body{"plan": "team"},
	},
}, openemail.WithIdempotencyKey("trials:2026-10-11"))
if err != nil {
	return err
}

for _, failure := range batch.Objects("failed") {
	fmt.Println(failure.Int("index"), failure.String("code"), failure.String("message"))
}

fmt.Println(len(batch.Objects("accepted")))
```

**Notes**

- A body of the wrong shape, an empty slice or more than 100 events is refused whole with 422 `invalid_parameter`, and `param` names the event and the field, such as `events.3.name`.
- The batch counts as that many events toward the 600 a minute a key or an app may send. A batch that would pass it is 429 `event_rate_limited` and nothing is stored.
- A failed event carries the code the single call would answer: `invalid_event`, `contact_not_found` or `idempotency_key_reuse`.
- Retried automatically on network failure and retryable statuses, because the idempotency key makes a retry safe.

Also available in: API [`POST /events/batch`](https://openemail.uk/docs/api/reference/events#post-events-batch); 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); 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).

### `Events.ListNames`

List the event names the workspace has seen

```go
ListNames(ctx context.Context, opts ...openemail.RequestOption) ([]openemail.Object, error)
```

Returns 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. The list is not paginated.

Scopes: `automations:read`.

**Parameters**

- `openemail.WithAPIKey` (`string`): Overrides the client's API key for this call only.

**Returns**

A `[]openemail.Object`, each with `name`.

**Example**

```go
names, err := client.Events.ListNames(ctx)
if err != nil {
	return err
}

for _, name := range names {
	fmt.Println(name.String("name"))
}
```

**Notes**

- It needs `automations:read`, not a contacts scope, because it serves the automation builder.
- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /events/names`](https://openemail.uk/docs/api/reference/events#get-events-names); 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); 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).
