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

# Platform events

> Understand the facts Nexio records about your organization, how each event is identified, and when events drive automations.

A platform event is one row in your organization's event log. It records that something happened: a dataset refreshed, a sync run finished, a data check refused a load, a document library was re-indexed, a schedule came due, a deploy finished in one of your systems, or anything else your own systems report through an inbound source. The log is append-only and scoped to one organization. Events are what [automations](/events/automations) react to.

Platform events are a capability Nexio operates for you. Nexio turns the event log on for your organization and configures the automations that use it. There is no public API to read the log; the only public route in this area is [inbound events](/events/inbound), which lets your systems append to it. Run results are not platform events; they reach you as [webhooks](/api-reference/webhooks/overview).

## How it works

1. A component inside Nexio records an event in the same database transaction as the write it describes, so a change that rolled back leaves no event.
2. The event is deduplicated on its identity. Recording the same fact twice leaves one event.
3. About every 5 minutes, Nexio's dispatcher reads events recorded in the last 24 hours that it has not yet evaluated and matches them against your organization's automations.
4. Each match is recorded as a dispatch with a stable ID. A dispatch that passes the automation's checks runs its effect. Retries reuse that ID; a webhook receiver still deduplicates, because a post can arrive twice. See [Automations](/events/automations).

## Event fields

| Field              | Meaning                                                                                                                                                                                                                                                                            |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`               | UUID of the event.                                                                                                                                                                                                                                                                 |
| `org_id`           | The organization the event belongs to. Always set by Nexio, never by a sender.                                                                                                                                                                                                     |
| `type`             | What happened, as `family.name`, for example `dataset.refreshed` or a type your own system chose. See [Event types](#event-types).                                                                                                                                                 |
| `subject`          | What it happened to, as `kind/id`, for example `dataset/served_markets` or `connection/5b1e7d3a-9c24-4f86-a0e2-3d8f6b1c7e49`. Automations can filter on a subject prefix.                                                                                                          |
| `body`             | A JSON object. Components inside Nexio write a small one of IDs, counts, and changed fields, never rows or files. An inbound event carries what the sender submitted, for example a `nexio` source's `body` object as sent, within the 1 MiB request limit. An empty body is `{}`. |
| `transition_cause` | Why the change happened. See [Transition causes](#transition-causes).                                                                                                                                                                                                              |
| `produced_by`      | Which class of emitter recorded it. See [Emitters](#emitters).                                                                                                                                                                                                                     |
| `caused_by`        | The parent event's `id`, when an automation emitted this event in reaction to another. Empty otherwise.                                                                                                                                                                            |
| `dedupe_key`       | Part of the event's identity. See [Identity and deduplication](#identity-and-deduplication).                                                                                                                                                                                       |
| `occurred_at`      | When the change happened in the world.                                                                                                                                                                                                                                             |
| `recorded_at`      | When Nexio wrote the event.                                                                                                                                                                                                                                                        |

### Identity and deduplication

An event's identity is its organization, `subject`, `type`, and `dedupe_key`. A second append with the same identity is dropped, and the first event stands. That is the designed result of a retry, not an error. Each of Nexio's emitters picks a dedupe key that names one real fact, for example the run ID for a dataset refresh, so an emitter that repeats the same write after a crash does not create a second event.

### Transition causes

| Cause                 | Meaning                                                                                                                                                                                                                          |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `world_change`        | Something changed in the world or in your data. Nexio's own emitters stamp this cause, and so does every inbound event. An automation's `emit_event` effect copies the matched event's cause unless the automation sets another. |
| `policy_reprojection` | A re-computation under changed rules, not a new fact.                                                                                                                                                                            |
| `merge_fold`          | A merge of records, not a new fact.                                                                                                                                                                                              |
| `backfill`            | An event recorded after the fact. An event a Nexio operator records by hand defaults to this cause.                                                                                                                              |

An automation fires only for the causes it lists, and by default it lists only `world_change`. A backfill therefore does not set off your automations unless Nexio configured one to accept it.

### Emitters

| `produced_by` | Emitter                                                              |
| ------------- | -------------------------------------------------------------------- |
| `run`         | A platform run of work: a data transformation, a sync, a data check. |
| `connector`   | A connection, for example a document library sync.                   |
| `schedule`    | The scheduler, for scheduled automations.                            |
| `rule`        | An automation's `emit_event` effect.                                 |
| `ingest`      | An [inbound event](/events/inbound) from one of your systems.        |
| `api`         | A Nexio operator appending by hand.                                  |

`produced_by` tells you whether an event came from inside Nexio or from outside. An event with `produced_by` set to `ingest` came from an inbound source, authenticated per source kind: by signature, or, for an `ams360_ons` source, by a shared authentication code.

### Lineage

When an automation emits a new event in reaction to another, the new event's `caused_by` holds the parent's `id`. The chain can be followed back to the original fact. The dispatcher uses the chain to stop loops: see [Loop refusal](/events/automations#loop-refusal).

## Event log modes

The event log has a mode per organization. Nexio sets it.

| Mode            | Events recorded                                                       | Automations run                                                                                      |
| --------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `off` (default) | No, except inbound events and events a Nexio operator records by hand | No                                                                                                   |
| `shadow`        | Yes                                                                   | No. A match is recorded as `skipped_shadow` instead, so Nexio can check an automation before it acts |
| `live`          | Yes                                                                   | Yes                                                                                                  |

Inbound events are recorded in every mode, including `off`. While an organization is `off`, they are stored but not dispatched.

## Event types

An event type is a name of the form `family.name`. The set is not closed: most types are chosen by the system that sends them, and the platform adds a small fixed set of its own.

### Types your systems choose

This is the extension point. Two kinds of event carry a type their sender chose:

* Events from a `nexio` [inbound source](/events/inbound) carry any of the types configured on that source. You name them when you ask Nexio for the source, for example `crm.account_updated` or `vendor.contract_signed`. Any name is accepted except a [reserved type](#reserved-types). The subject and body are the sender's, and `produced_by` is `ingest`.
* Events from an automation's `emit_event` effect carry the type configured on the automation, with `produced_by` set to `rule`, `caused_by` set, and the matched event's `id` in the body as `source_event_id`.

`eval.run.requested` is one such type with a platform meaning. An automation emits it to start an [evaluation](/engines/evaluation). For an engine evaluation its body carries `eval_set_id`, `reason`, and optional `engine_slug`, `engine_id`, `commit_sha`, and `deployed_at`.

### Types the platform records

The platform's own emitters record a fixed set of types, defined in code. Each has a fixed subject form and body.

### dataset.refreshed

A platform data transformation finished and loaded a new version of a dataset.

|            |                                                                                                                                                                                                                                                                                                                                      |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Subject    | `dataset/<dataset_key>`                                                                                                                                                                                                                                                                                                              |
| Emitter    | `run`                                                                                                                                                                                                                                                                                                                                |
| Dedupe key | The run ID                                                                                                                                                                                                                                                                                                                           |
| Body       | `dataset_key`, `run_id`, `batch_or_version` (the identity of the new version), `rows_landed` (rows loaded), `prior_version` (optional; empty when unknown, never "there was none"), `connection_id` (optional), `derive_version` (optional; the transformation code's version, so you can tell a data refresh from a re-computation) |

```json theme={null}
{
  "dataset_key": "served_markets",
  "run_id": "a7f2c9e1-3b58-4d06-9c4e-1e8b7d2f6a93",
  "batch_or_version": "2026-09-23T13:00:00Z",
  "rows_landed": 4127,
  "derive_version": "3"
}
```

### sync.run\_completed

A sync or transformation run finished, whatever its result.

|            |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Subject    | `run/<run_id>`                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| Emitter    | `run`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| Dedupe key | The run ID plus the status                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| Body       | `run_kind` (`book_sync`, `materialize_sync`, or `derivation`), `dataset_key` (optional), `connection_id` (optional), `status` (`succeeded`, `failed`, `refused`, or `skipped_unchanged` when a scheduled `book_sync` found the source unchanged and ran nothing), `error_class` (optional; set on a failed `book_sync` run), `duration_ms`, `trigger` (what started the run, for example `monitor` for the continuous update monitor, `manual`, or `initial` for a connection's first index) |

`run_kind` names the kind of work: `book_sync` is a sync of a connected system of record, `materialize_sync` is a document library pass, and `derivation` is a data transformation. See [Data transformation and delivery](/connections/delivery).

```json theme={null}
{
  "run_kind": "materialize_sync",
  "connection_id": "5b1e7d3a-9c24-4f86-a0e2-3d8f6b1c7e49",
  "status": "succeeded",
  "duration_ms": 184230,
  "trigger": "monitor"
}
```

### check.failed

A data check on a load or a delivery did not pass. A blocking check that failed stopped the load or delivery; a non-blocking check held some rows out.

|            |                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Subject    | `dataset/<dataset_key>`                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| Emitter    | `run`                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| Dedupe key | The `run_id`, the check key, and the check subject                                                                                                                                                                                                                                                                                                                                                                                                                  |
| Body       | `check_kind` (`k_floor`, `row_width`, `crosscheck`, `contract_complete`, `freshness`, `volume_shrink`, `reconciliation`, or `domain_rule`), `check_key`, `dataset_key` (optional), `check_subject` (optional; which evaluation of a check that runs many times), `run_id` (the run, or for a check at a delivery boundary the ID of the recorded verdict), `verdict` (`refused`: the load or delivery did not happen; `quarantined`: rows were held back), `detail` |

```json theme={null}
{
  "check_kind": "volume_shrink",
  "check_key": "served_markets.volume_shrink",
  "dataset_key": "served_markets",
  "run_id": "a7f2c9e1-3b58-4d06-9c4e-1e8b7d2f6a93",
  "verdict": "refused",
  "detail": "row count fell below the declared shrink limit against the prior version"
}
```

### records.changed

The rows of a served dataset changed. Today one dataset produces it, `served_commission_terms`.

|            |                                                                                                                                                                                                                                                  |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Subject    | `dataset/<dataset_key>`                                                                                                                                                                                                                          |
| Emitter    | `run`                                                                                                                                                                                                                                            |
| Dedupe key | The identity of the changed record group and a hash of the created and retired row IDs                                                                                                                                                           |
| Body       | `dataset_key`, `run_id` (empty when the change is a document's records being removed rather than a run's output), `change_summary` (`adds`, `removes`, `changes`), `changed_keys_ref` (a pointer to the changed rows, never the rows themselves) |

```json theme={null}
{
  "dataset_key": "served_commission_terms",
  "run_id": "d4b8e2f7-1a39-4c65-8e0d-7f2a9b5c3e16",
  "change_summary": { "adds": 12, "removes": 3, "changes": 0 },
  "changed_keys_ref": "example-entity-key"
}
```

### document.index\_changed

A document library sync added, changed, or removed indexed documents. It is not recorded when nothing changed.

|            |                                                                 |
| ---------- | --------------------------------------------------------------- |
| Subject    | `connection/<connection_id>`                                    |
| Emitter    | `connector`                                                     |
| Dedupe key | The sync run ID                                                 |
| Body       | `connection_id`, `sync_run_id`, `added`, `changed`, `deindexed` |

```json theme={null}
{
  "connection_id": "5b1e7d3a-9c24-4f86-a0e2-3d8f6b1c7e49",
  "sync_run_id": "c83f1a6e-5d27-4b90-9e4a-2f7c8d1b6e05",
  "added": 14,
  "changed": 3,
  "deindexed": 1
}
```

See [Document indexing and extraction](/connections/indexing-and-extraction).

### schedule.fired

A scheduled automation came due. It starts that automation.

|            |                                                               |
| ---------- | ------------------------------------------------------------- |
| Subject    | `subscription/<automation_id>`                                |
| Emitter    | `schedule`                                                    |
| Dedupe key | The slot start time, so a slot fires once                     |
| Body       | `subscription_id`, `key` (the automation's key), `slot_start` |

```json theme={null}
{
  "subscription_id": "1f6a9d3c-7e28-4b54-a0c1-8d2e5f7b9a60",
  "key": "nightly-eval-vendor-review",
  "slot_start": "2026-09-24T02:00:00Z"
}
```

### Types minted by verified inbound sources

Two types are recorded only from an inbound source whose kind Nexio verifies, never from a `nexio` source:

* `code.deployed`, from a `github` source (below).
* `ams360.notification_received`, from an `ams360_ons` source: notifications from AMS360, a supported system type. Its subject is `ams360_ons/<source_id>`, and its body records the delivery (among other fields, the payload's SHA-256, size and format) and the payload with the authentication code removed; the payload is left out when it is over 64 KiB after that, or when the code cannot be removed from it.

### code.deployed

A GitHub deployment reached a status the inbound source accepts (by default `success`) for a repository connected as an inbound source. Nexio records it from GitHub's `deployment_status` webhook. See [Inbound events](/events/inbound#github-deployments).

|            |                                                                                                                                                                                                    |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Subject    | `repository/<owner>/<name>`                                                                                                                                                                        |
| Emitter    | `ingest`                                                                                                                                                                                           |
| Dedupe key | `deployment/<environment>/<commit_sha>`, so one commit deployed to one environment is one event                                                                                                    |
| Body       | `repository`, `environment`, `status`, `commit_sha`, `ref`, `deployment_id`, `deployment_status_id`, `deployed_at`, `delivery_id`, `environment_url` (optional), `log_url` (optional), `source_id` |

```json theme={null}
{
  "repository": "harbor-group/supplier-portal",
  "environment": "production",
  "status": "success",
  "commit_sha": "4e1d9b7a2c68f3051e9a7d4b2c8f6e1a3d5b7c90",
  "ref": "main",
  "deployment_id": "1873459921",
  "deployment_status_id": "2934875112",
  "deployed_at": "2026-09-23T16:42:08Z",
  "delivery_id": "72d1e6b0-9a4f-11ef-8c3d-5e7a1b2c9d40",
  "environment_url": "https://suppliers.harbor-group.example",
  "source_id": "b91c4e7a-3d62-4f08-a5e1-6c2d9f8b0a37"
}
```

### Reserved types

A `nexio` inbound source can never append these types, because each one asserts that Nexio itself observed something. Nexio records `code.deployed` and `ams360.notification_received` only from a verified `github` or `ams360_ons` source:

`schedule.fired`, `eval.run.requested`, `book.published`, `dataset.refreshed`, `records.changed`, `check.failed`, `sync.run_completed`, `document.index_changed`, `code.deployed`, `ams360.notification_received`.

`book.published` names a publish of system-of-record data. It is reserved, and no emitter records it today.

## What you configure and what you call

* **You configure:** nothing directly. Tell Nexio which facts you want to react to. Nexio sets the event log mode and the automations for your organization.
* **You call:** `POST /api/v1/events/ingest/{source_key}` to send your own events. See [Inbound events](/events/inbound).
* **You receive:** platform events on one of your webhook endpoints, when Nexio configures an automation with the `deliver_webhook` effect. See [Platform event deliveries](/api-reference/webhooks/overview#platform-event-deliveries-to-the-same-endpoint).

<CardGroup cols={2}>
  <Card title="Automations" href="/events/automations">
    How events trigger work, with budgets, retries, and loop protection.
  </Card>

  <Card title="Inbound events" href="/events/inbound">
    Send authenticated events from your systems.
  </Card>
</CardGroup>
