> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trybrein.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Brein events

> The ledger of what happens in your workspace: what emits an event, its shape and how to read it.

Every change that matters in your workspace is written down as an event: something arrived, a Contact was created, an Opportunity was won. The event is written **in the same transaction as the change**: if it is not recorded, the change does not happen either. That lets an integration or an agent follow what happens without missing anything.

You can **read** it whenever you want ([below](#read-the-ledger)), **receive** it on your server with a [Destination](/api/destinations), or **subscribe** from an agent with [MCP Events](/mcp/events).

## What emits an event

A durable change to an object emits one: it is created, changes state, is linked to another object, has fields filled, or something that arrived finishes processing.

Reading, searching, quoting and proposing do not (the event is what was done, not the proposal), nor do screen conveniences or retries that changed nothing.

## The shape

Each event is a [CloudEvent 1.0](https://cloudevents.io):

```json theme={"theme":"github-light"}
{
  "specversion": "1.0",
  "id": "4d6f0c2e-6a1b-5f7e-9c3d-2b8a7e1f0a55",
  "source": "https://api.trybrein.com/workspaces/9a0e…",
  "type": "opportunity.moved",
  "subject": "opportunity/7b2c…",
  "time": "2026-10-15T18:04:11.000Z",
  "datacontenttype": "application/json",
  "dataschema": "https://trybrein.com/schemas/events/opportunity.moved/v0",
  "data": { "pipeline": "c1d9…", "from": "Proposal", "to": "Won", "outcome": "won" },
  "workspace": "9a0e…",
  "actorkind": "human",
  "actor": "user_2f…",
  "via": "app",
  "causationid": "e81a…",
  "correlationid": "0b44…"
}
```

| Field | What it is |
| - | - |
| `id` | Unique. The same `id` twice is the same event. |
| `type` | `object.verb`. See the [catalog](#catalog). |
| `subject` | `object/id` of the object that changed. |
| `time` | When it happened. |
| `dataschema` | The public JSON Schema for that type. |
| `workspace` | Your workspace. |
| `actorkind` | `human`, `agent` or `system`. |
| `actor` | Who: the person, the agent or the Source. May be absent. |
| `via` | Through what: `app`, `mcp`, `api`, `door` or `routine`. |
| `causationid` | The event that caused this one. May be absent on the first of a chain. An agent that writes back after a delivery sets it with the `X-Brein-Cause` header (see [Destinations](/api/destinations#an-agent-that-answers-never-wakes-itself)). |
| `correlationid` | The first event of the chain. Follow a sale back to what started it. |
| `runid` | An agent's run, if there was one. |

`data` carries references (ids) and what changed (field names, previous and new stage, the previous lifecycle stage or owner), **never email or phone**, up to 20 KiB. Anyone with permission reads the full record through the API or the MCP.

## Catalog

| `type` | When | `data` |
| - | - | - |
| `arrival.pending_decision` | The identity is ambiguous; waits for a person. | `candidates` (count) |
| `arrival.failed` | Attempts ran out. | `code`, `attempts` |
| `sequence.replied` | A Contact replied to a Sequence send. | `sequence`, `contact`, `send`; optional `opportunity` |
| `contact.created` | A Contact was created. | `origin`; optional `company` |
| `contact.updated` | Its email, phone, LinkedIn, lifecycle stage or owner changed. Any other field change emits nothing. | `fields` (names, never values); optional `previous` (the old `lifecycle_stage` and `owner`) |
| `company.created` | A Company was created. | `domain` |
| `opportunity.created` | An Opportunity was created. | `pipeline`, `stage`; optional `company`, `contacts` |
| `opportunity.moved` | It changed stage. | `pipeline`, `from`, `to`, `outcome` (`won`, `lost` or `null`) |

The path of an arrival (received, decided, accepted, rejected, quarantined, reprocessed) is not an event: read it with `arrival_read`. The exact schema of each, envelope included, is at `https://trybrein.com/schemas/events/<type>/v0`. Ids are strings; a field not in the schema is not accepted.

## Read the ledger

```bash theme={"theme":"github-light"}
curl "https://api.trybrein.com/api/v1/workspaces/<slug>/events?limit=50" \
  -H "X-API-Key: $BREIN_API_KEY"
```

```json theme={"theme":"github-light"}
{ "data": [ { "specversion": "1.0", "id": "…", "type": "contact.created" } ], "next_cursor": "MTIzOjQ1", "has_more": true }
```

| Parameter | What it does |
| - | - |
| `after` | The `next_cursor` from the previous page. Without it, starts from the beginning. |
| `types` | Comma-separated types: `opportunity.moved,contact.updated`. |
| `subject` | One exact object: `opportunity/<id>`. |
| `limit` | 1 to 100. Default 50. |

To stay current, keep `next_cursor` and ask again with `after`. When there is nothing new, the response has empty `data` and the same cursor: keep using it. **The cursor never skips an event**: an event appears only once its transaction and every earlier one have finished, so anything that lands late always comes after your cursor.

One event by id: `GET …/workspaces/<slug>/events/<id>`.

| Error | When |
| - | - |
| `400` `invalid_cursor` | The cursor is not one Brein gave you. |
| `400` `unknown_event_type` | A type in `types` is not in the catalog. |
| `404` `event_not_found` | That event does not exist in your workspace. |

Any workspace member can read its ledger. An API key or a connected app reads it with read permission.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.