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

# Events & webhooks overview

> How the durable event feed and lossy wakeup webhooks fit together.

The eventing model has two halves with deliberately different guarantees:

1. **The event feed is the source of truth.** `GET /api/v1/events` is a durable, cursor-ordered pull feed of everything that happened in your organization (within your key's scopes). Events are written in the same database transaction as the mutation that caused them, so the feed never misses a change.
2. **Webhooks are lossy wakeups.** A webhook delivery tells you *that* new events are available — never *what* happened. The payload is minimal by design; on receiving one (and on your own schedule) you poll the feed from your last cursor. Webhooks can be lost, replayed, or delayed without correctness consequences, and no sensitive data ever transits your endpoint.

This split means a missed webhook is harmless: cursor-resume on the feed always catches you up. We recommend polling `GET /events` at least every 5 minutes even with webhooks configured.

## The event envelope

```json theme={null}
{
  "id": "9f2b7c3e-0000-4000-8000-000000000000",
  "cursor": "123",
  "entity": "case",
  "entityId": "case-example-id",
  "eventType": "case.status_changed",
  "occurredAt": "2026-08-11T12:00:00Z",
  "actor": { "id": "key-or-user-id", "type": "service", "name": "Example CRM sync" },
  "payload": { "caseId": "case-example-id", "previousStatus": "…", "newStatus": "…" },
  "diff": null,
  "relatedIds": [{ "entity": "case", "id": "case-example-id" }],
  "correlationId": null,
  "metadata": { "origin": "…" }
}
```

* `id` is the **dedup key**: the feed is at-least-once, so treat events idempotently.
* `cursor` is strictly monotonic per organization. Pass the last cursor you processed as `?after=` to resume.
* `actor` + `metadata.origin` support **echo suppression**: skip events your own integration caused by comparing `actor.id` to your key id.
* All payloads and diffs pass sensitive-field redaction before they reach the feed. Events marked *ids-only* in the [catalog](/docs/webhooks/event-catalog) carry an empty payload — fetch details via the scoped resource endpoints.

## Reading the feed

`GET /api/v1/events` requires `events:read`, and results are additionally **scope-filtered**: a key only sees events for entities whose required read scope it also holds (e.g. `case.*` events require `cases:read`; medical events require `medical:read`). Filters: `entity`, `eventType`, `caseId`, plus standard cursor pagination.

```bash theme={null}
curl "https://attorney.dearlegal.com/api/v1/events?after=123&limit=200" \
  -H "x-api-key: YOUR_API_KEY"
```

`GET /api/v1/document-events` is the original documents-only feed and remains available as a deprecated alias for shipped document-sync integrations.

## Subscriptions

Webhook subscriptions are managed with the `webhooks:manage` scope and are scoped to your API key — sibling keys cannot see each other's subscriptions.

* `POST /api/v1/webhook-subscriptions` — `{ "url": "https://...", "eventTypes": [...] }`. `eventTypes` accepts public event types and `entity.*` wildcards (e.g. `case.*`), validated against the [public catalog](/docs/webhooks/event-catalog); omitted, you receive the default wakeup.
* The subscription **signing secret is returned once**, on create — and again only when you rotate it (`PATCH` with `"rotateSecret": true`).
* The URL must be HTTPS on a host in your key's **webhook allowed hosts** allowlist (configured on the key; fail-closed when empty).

Delivery mechanics, signature verification, and retry semantics: [Delivery](/docs/webhooks/delivery).

## Ordering & delivery guarantees

* Feed cursors are strictly monotonic per organization, and a later cursor never becomes readable before an earlier one.
* The feed is **at-least-once** — dedupe on envelope `id`.
* Event retention is currently indefinite, but do not build on unbounded replay; a retention window may be introduced with advance notice.
