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

# Events and webhooks

> An ordered, replayable log of everything that changes in a subscription

<Info>
  Data Feeds is in early access. [Contact us](mailto:support@ploti.ai) to set up
  a subscription; the API and sync client described here open to early access
  customers first.
</Info>

Each subscription has an event log. Events are ordered, each has an id that increases, and you read them after the last one you applied. Webhooks deliver the same events as they happen, so you do not have to poll.

## Event types

| Type | Sent when | `data` includes |
| - | - | - |
| `dataset.added` | A dataset joins the subscription. | `datasetId`, `version`, `schemaVersion` |
| `dataset.updated` | A dataset has a new version. | `version`, `previousVersion`, `counts`, `links` |
| `dataset.schema_changed` | A new version changes the schema. Sent before that version's `dataset.updated`. | `schemaVersion`, `schemaChange` |
| `dataset.resync_required` | The dataset must be reloaded from a snapshot. | `version`, `reason` |
| `dataset.removed` | A dataset leaves the subscription. | `reason` |

`reason` is one of `key_columns_changed`, `cursor_expired`, `left_coverage`, `subscription_changed` or `source_retired`.

```json theme={null}
{
  "id": "evt_0000018f2b11",
  "type": "dataset.updated",
  "createdAt": "2026-10-18T06:12:41Z",
  "subscriptionId": "sub_8f3k2m",
  "data": {
    "datasetId": "ds_orange_fl_building_permits",
    "version": 42,
    "previousVersion": 41,
    "schemaVersion": "2.1",
    "counts": { "added": 212, "changed": 38, "removed": 3 },
    "links": {
      "version": "https://api.ploti.ai/v1/datasets/ds_orange_fl_building_permits/versions/42",
      "changes": "https://api.ploti.ai/v1/datasets/ds_orange_fl_building_permits/versions/42/changes",
      "schema": "https://api.ploti.ai/v1/datasets/ds_orange_fl_building_permits/schema?version=42"
    }
  }
}
```

## Reading the log

```bash theme={null}
curl "https://api.ploti.ai/v1/subscriptions/sub_8f3k2m/events?after=evt_0000018f2b10&limit=100" \
  -H "Authorization: Bearer $PLOTI_API_KEY"
```

Returns `events` and `hasMore`. Apply events in order, and store the id of the last one you applied only after it is applied. A crash then repeats at most one event, and every event is safe to apply twice.

Events are kept for 90 days. An `after` older than that returns `410`: reload from the subscription's manifest, which gives a fresh cursor.

## Webhooks

Create a webhook for the subscription's events:

```bash theme={null}
curl -X POST https://api.ploti.ai/v1/webhooks \
  -H "Authorization: Bearer $PLOTI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://acme.example/hooks/ploti",
    "events": ["dataset.added", "dataset.updated", "dataset.schema_changed", "dataset.resync_required", "dataset.removed"],
    "subscriptionId": "sub_8f3k2m"
  }'
```

Each POST is signed with [Standard Webhooks](https://www.standardwebhooks.com), using the secret returned once when the webhook is created. Verify the signature, answer `2xx` quickly and do the work afterwards. Failed deliveries are retried with backoff.

Treat a webhook as the signal to read the log, not as the record. If your receiver was down, the log still has every event.


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