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

# Webhook events and delivery

> Signed event, digest and retention-gap payloads with retry and recovery behavior.

Receive signed JSON notifications, then use authenticated event feeds as the durable source. Delivery must be active in the selected hosted environment. Configure endpoints through [create\_webhook](/reference/commands/create_webhook).

## Request headers and verification

| Header                | Meaning                                         |
| --------------------- | ----------------------------------------------- |
| Content-Type          | application/json                                |
| User-Agent            | LinktrailWebhook/1                              |
| Linktrail-Timestamp   | Unix seconds as a string                        |
| Linktrail-Signature   | One or two space-separated v1=HEX signatures    |
| Linktrail-Delivery-Id | Stable whd\_ delivery identifier across retries |
| Linktrail-Event-Type  | Event type; empty for digests                   |

Compute HMAC-SHA-256 using the full whsec\_ secret over the timestamp, a literal dot, and the exact raw UTF-8 body. Compare hex signatures in constant time. Reject invalid timestamps or timestamps more than 300 seconds away from the receiver clock. During rotation, accept a match against either active secret and any offered signature.

Persist the raw notification or enqueue it durably before answering 2xx. Deduplicate work by Linktrail-Delivery-Id in durable storage. Do not mark a delivery processed before its work has been durably accepted. The event ID supplies a separate identity when reconciling with pulled feed records.

## Per-event payload

```json theme={null}
{
  "v": 1,
  "feed": "events",
  "type": "placement_acquired",
  "id": "evt_example",
  "sequence": 42,
  "workspace_id": "ws_example",
  "project_id": "prj_example",
  "subject_id": "watch_example",
  "created_at": "2026-09-13T12:00:00.000Z",
  "transition": "present",
  "previous": null,
  "uncertain": false,
  "checked_at": "2026-09-13T12:00:00.000Z",
  "observation_id": "obs_example",
  "evidence_key": null,
  "resume": {"feed":"events","after_sequence":42,"endpoint":"/v1/events"}
}
```

This is an illustrative shape, not a live event. `feed` is events or target\_events. `subject_id` identifies the watch or destination. `transition` is the resulting state; `previous` is the prior state. State, uncertainty, checked time, observation identifier and evidence reference may be null when absent. Evidence references contain no publisher page bytes. Read evidence with your own authenticated credential; expired bytes return EVIDENCE\_EXPIRED.

`resume.feed` identifies the independent feed. `resume.after_sequence` gives the sequence position for recovery through the named endpoint. Maintain your own durable checkpoint from the authenticated feed so a delivered notification cannot cause you to skip earlier unseen events.

## Event types

| Feed           | Type                           | Trigger                                            |
| -------------- | ------------------------------ | -------------------------------------------------- |
| events         | placement\_acquired            | First conclusive present result                    |
| events         | placement\_recovered           | Present after confirmed loss/source unavailability |
| events         | placement\_changed             | Present link fingerprint changes                   |
| events         | placement\_lost                | Confirmed absence after prior presence             |
| events         | source\_unavailable            | Confirmed source unavailability                    |
| events         | watch.checked                  | First check with no named transition               |
| events         | watch.state\_changed           | State changes with no named transition             |
| events         | watch.check\_quality\_changed  | Uncertainty or uncertain reason changes            |
| target\_events | target\_healthy                | First conclusive healthy destination               |
| target\_events | target\_recovered              | Healthy after confirmed unavailability             |
| target\_events | target\_unavailable            | Confirmed destination unavailability               |
| target\_events | target.checked                 | First check with no named transition               |
| target\_events | target.state\_changed          | State changes with no named transition             |
| target\_events | target.check\_quality\_changed | Check uncertainty/reason changes                   |

Confirmed absence/unavailability requires repeated conclusive evidence according to the relevant reducer; a blocked or incomplete check remains uncertain. Endpoint event-type filters advance their cursor across filtered-out types. A syntactically accepted type with no emitter matches no events.

## Digest payload

Digest mode summarizes the events feed over daily or weekly windows. A payload contains:

| Field                                          | Contract                                                          |
| ---------------------------------------------- | ----------------------------------------------------------------- |
| v, kind, workspace\_id                         | Version 1, kind digest and owning workspace                       |
| window\.opened\_at, window\.closed\_at         | Window boundaries                                                 |
| computed                                       | true when summary was computed, even for quiet windows            |
| totals.subjects, totals.events, totals.changed | Counts over events read for this digest                           |
| subjects                                       | Up to 200 subject summaries                                       |
| truncated                                      | More than 200 subjects                                            |
| events\_truncated                              | More than the bounded 1000-event read                             |
| resume                                         | Feed, last read sequence or null for a quiet window, and endpoint |

Each subjects row has subject\_id, project\_id, previous, state, uncertain, events, types, first\_seen\_at, last\_seen\_at, checked\_at and evidence\_key. A subject that changes repeatedly appears once with its final state and its event count. The read limit and subject limit are separate; inspect both flags. Quiet windows are delivered with computed:true and zero totals. Digest notifications use the same signature, delivery ID and retry mechanism.

## Retention gap payload

Dispatch on `kind` before treating a message as a normal event. When retention overtakes an endpoint cursor, a `kind:"history_gap"` notification has type webhook.history\_gap and code CURSOR\_EXPIRED. It includes id, feed, workspace\_id, project\_id, created\_at, computed:false, totals:null and resync\_required:true.

`history_gap` contains after\_sequence, through\_sequence, scope:"workspace\_feed", reason:"history\_unavailable" and affected\_project\_events:"unknown". `snapshot_endpoint` names the current-state read; `resume_cursor` is the supplied opaque resume token. `resume` carries feed, after\_sequence and endpoint. A digest gap also carries delivery\_mode:"digest" and window. Preserve the gap explicitly. A current snapshot cannot recover expired history.

The [local receive command](/reference/local/receive) currently accepts per-event identity only. Handle digest and history-gap notifications in your receiver and use the authenticated feed/snapshot endpoints for recovery.

## Retries and recovery

A 2xx response completes a delivery. HTTP 410 disables the endpoint immediately with receiver\_reported\_gone. Other failures retry with a ten-second send timeout and exponential backoff `min(3600, 15 * 2^min(attempt,8))` seconds. Actual delivery ticks can make retries later than that minimum. Twenty consecutive failures disable the endpoint; a successful delivery resets the count. Oversized payloads above 256 KiB fail terminally.

Read [list\_webhook\_deliveries](/reference/commands/list_webhook_deliveries) to inspect failures. Setting an endpoint active resumes pending work, but does not resend terminal failed rows. Recover missing notifications through [list\_link\_events](/reference/commands/list_link_events) and [list\_target\_events](/reference/commands/list_target_events), using separate cursors. Terminal delivery rows are retained for 30 days after becoming terminal; pending rows are not pruned by that terminal-history policy.

## Rotation and endpoint changes

Call [rotate\_webhook\_secret](/reference/commands/rotate_webhook_secret) with retire:false, save the once-returned secret, and deploy receiver support for both secrets. After verifying new-secret delivery, call it with retire:true to retire the older secret. A second rotation is refused while a rotation is open.

An endpoint URL is not an editable field. Create and verify a replacement endpoint, then disable or delete the old endpoint. Deduplicate notifications while both operate. Review [HTTP route differences](/reference/http/routes) if using resource aliases.

<Note>This reference describes the development delivery contract. Check hosted availability before depending on delivery.</Note>

## Downloadable notification schemas

These examples come from the actual payload builders with fixture records. They are not live deliveries. Inspect kind and feed before reading a payload.

### event

[Download event schema](/schemas/webhook-event.json)

```json theme={null}
{
  "v": 1,
  "feed": "events",
  "type": "placement_acquired",
  "id": "evt_example",
  "sequence": 42,
  "workspace_id": "ws_example",
  "project_id": "prj_example",
  "subject_id": "watch_example",
  "created_at": "2026-09-13T12:00:00.000Z",
  "transition": "present",
  "previous": "unknown",
  "uncertain": false,
  "checked_at": "2026-09-13T12:00:00.000Z",
  "observation_id": "obs_example",
  "evidence_key": "evidence/example.json",
  "resume": {
    "feed": "events",
    "after_sequence": 42,
    "endpoint": "/v1/events"
  }
}
```

| Field                   | Type           | Presence | Meaning and constraints                                                                                                                                                                                                                                   |
| ----------------------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `v`                     | number         | Required | See the typed schema and response example for this field. must equal 1                                                                                                                                                                                    |
| `feed`                  | string         | Required | See the typed schema and response example for this field. must equal "events"                                                                                                                                                                             |
| `type`                  | string         | Required | See the typed schema and response example for this field. values: "placement\_acquired", "placement\_recovered", "placement\_changed", "placement\_lost", "source\_unavailable", "watch.checked", "watch.state\_changed", "watch.check\_quality\_changed" |
| `id`                    | string         | Required | See the typed schema and response example for this field.                                                                                                                                                                                                 |
| `sequence`              | integer        | Required | See the typed schema and response example for this field. minimum: 1                                                                                                                                                                                      |
| `workspace_id`          | string         | Required | Identifier returned by the related operation.                                                                                                                                                                                                             |
| `project_id`            | string         | Required | Identifier returned by the related operation.                                                                                                                                                                                                             |
| `subject_id`            | string         | Required | Identifier returned by the related operation.                                                                                                                                                                                                             |
| `created_at`            | string         | Required | See the typed schema and response example for this field. pattern: "^\d\{4}-\d\{2}-\d\{2}T.\*(?:Z\|\[+-]\d\{2}:\d\{2})\$"                                                                                                                                 |
| `transition`            | string / null  | Required | See the typed schema and response example for this field.                                                                                                                                                                                                 |
| `previous`              | string / null  | Required | See the typed schema and response example for this field.                                                                                                                                                                                                 |
| `uncertain`             | boolean / null | Required | See the typed schema and response example for this field.                                                                                                                                                                                                 |
| `checked_at`            | string / null  | Required | See the typed schema and response example for this field.                                                                                                                                                                                                 |
| `observation_id`        | string / null  | Required | Identifier returned by the related operation.                                                                                                                                                                                                             |
| `evidence_key`          | string / null  | Required | See the typed schema and response example for this field.                                                                                                                                                                                                 |
| `resume`                | object         | Required | See the typed schema and response example for this field. additional fields rejected                                                                                                                                                                      |
| `resume.feed`           | string         | Required | See the typed schema and response example for this field. must equal "events"                                                                                                                                                                             |
| `resume.after_sequence` | integer        | Required | See the typed schema and response example for this field. minimum: 1                                                                                                                                                                                      |
| `resume.endpoint`       | string         | Required | See the typed schema and response example for this field. must equal "/v1/events"                                                                                                                                                                         |
| `feed`                  | string         | Required | See the typed schema and response example for this field. must equal "target\_events"                                                                                                                                                                     |
| `type`                  | string         | Required | See the typed schema and response example for this field. values: "target\_healthy", "target\_recovered", "target\_unavailable", "target.checked", "target.state\_changed", "target.check\_quality\_changed"                                              |
| `evidence_key`          | null           | Required | See the typed schema and response example for this field.                                                                                                                                                                                                 |
| `resume.feed`           | string         | Required | See the typed schema and response example for this field. must equal "target\_events"                                                                                                                                                                     |
| `resume.endpoint`       | string         | Required | See the typed schema and response example for this field. must equal "/v1/target-events"                                                                                                                                                                  |

### digest

[Download digest schema](/schemas/webhook-digest.json)

```json theme={null}
{
  "v": 1,
  "kind": "digest",
  "workspace_id": "ws_example",
  "window": {
    "opened_at": "2026-09-12T12:00:00.000Z",
    "closed_at": "2026-09-13T12:00:00.000Z"
  },
  "computed": true,
  "totals": {
    "subjects": 1,
    "events": 1,
    "changed": 1
  },
  "subjects": [
    {
      "subject_id": "watch_example",
      "project_id": "prj_example",
      "previous": "unknown",
      "state": "present",
      "uncertain": false,
      "events": 1,
      "types": [
        "placement_acquired"
      ],
      "first_seen_at": "2026-09-13T12:00:00.000Z",
      "last_seen_at": "2026-09-13T12:00:00.000Z",
      "checked_at": "2026-09-13T12:00:00.000Z",
      "evidence_key": "evidence/example.json"
    }
  ],
  "truncated": false,
  "events_truncated": false,
  "resume": {
    "feed": "events",
    "after_sequence": 42,
    "endpoint": "/v1/events"
  }
}
```

| Field                      | Type           | Presence | Meaning and constraints                                                                                                   |
| -------------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `v`                        | number         | Required | See the typed schema and response example for this field. must equal 1                                                    |
| `kind`                     | string         | Required | See the typed schema and response example for this field. must equal "digest"                                             |
| `workspace_id`             | string         | Required | Identifier returned by the related operation.                                                                             |
| `window`                   | object         | Required | See the typed schema and response example for this field. additional fields rejected                                      |
| `window.opened_at`         | string         | Required | See the typed schema and response example for this field. pattern: "^\d\{4}-\d\{2}-\d\{2}T.\*(?:Z\|\[+-]\d\{2}:\d\{2})\$" |
| `window.closed_at`         | string         | Required | See the typed schema and response example for this field. pattern: "^\d\{4}-\d\{2}-\d\{2}T.\*(?:Z\|\[+-]\d\{2}:\d\{2})\$" |
| `computed`                 | boolean        | Required | See the typed schema and response example for this field. must equal true                                                 |
| `totals`                   | object         | Required | See the typed schema and response example for this field. additional fields rejected                                      |
| `totals.subjects`          | integer        | Required | See the typed schema and response example for this field. minimum: 0                                                      |
| `totals.events`            | integer        | Required | See the typed schema and response example for this field. minimum: 0                                                      |
| `totals.changed`           | integer        | Required | See the typed schema and response example for this field. minimum: 0                                                      |
| `subjects`                 | array          | Required | See the typed schema and response example for this field. maxItems: 200                                                   |
| `subjects[].subject_id`    | string         | Required | Identifier returned by the related operation.                                                                             |
| `subjects[].project_id`    | string         | Required | Identifier returned by the related operation.                                                                             |
| `subjects[].previous`      | string / null  | Required | See the typed schema and response example for this field.                                                                 |
| `subjects[].state`         | string / null  | Required | See the typed schema and response example for this field.                                                                 |
| `subjects[].uncertain`     | boolean / null | Required | See the typed schema and response example for this field.                                                                 |
| `subjects[].events`        | integer        | Required | See the typed schema and response example for this field. minimum: 1                                                      |
| `subjects[].types`         | array          | Required | See the typed schema and response example for this field. minItems: 1                                                     |
| `subjects[].first_seen_at` | string         | Required | See the typed schema and response example for this field. pattern: "^\d\{4}-\d\{2}-\d\{2}T.\*(?:Z\|\[+-]\d\{2}:\d\{2})\$" |
| `subjects[].last_seen_at`  | string         | Required | See the typed schema and response example for this field. pattern: "^\d\{4}-\d\{2}-\d\{2}T.\*(?:Z\|\[+-]\d\{2}:\d\{2})\$" |
| `subjects[].checked_at`    | string / null  | Required | See the typed schema and response example for this field.                                                                 |
| `subjects[].evidence_key`  | string / null  | Required | See the typed schema and response example for this field.                                                                 |
| `truncated`                | boolean        | Required | See the typed schema and response example for this field.                                                                 |
| `events_truncated`         | boolean        | Required | See the typed schema and response example for this field.                                                                 |
| `resume`                   | object         | Required | See the typed schema and response example for this field. additional fields rejected                                      |
| `resume.feed`              | string         | Required | See the typed schema and response example for this field. must equal "events"                                             |
| `resume.after_sequence`    | integer / null | Required | See the typed schema and response example for this field.                                                                 |
| `resume.endpoint`          | string         | Required | See the typed schema and response example for this field. must equal "/v1/events"                                         |

### historyGap

[Download historyGap schema](/schemas/webhook-historyGap.json)

```json theme={null}
{
  "v": 1,
  "kind": "history_gap",
  "type": "webhook.history_gap",
  "code": "CURSOR_EXPIRED",
  "id": "whgap_whe_example_events_20",
  "feed": "events",
  "workspace_id": "ws_example",
  "project_id": "prj_example",
  "created_at": "2026-09-13T12:00:00.000Z",
  "computed": false,
  "totals": null,
  "resync_required": true,
  "history_gap": {
    "after_sequence": 5,
    "through_sequence": 20,
    "scope": "workspace_feed",
    "reason": "history_unavailable",
    "affected_project_events": "unknown"
  },
  "snapshot_endpoint": "/v1/exports/watches?projectId=prj_example",
  "resume_cursor": "eyJ2IjoxLCJ3Ijoid3NfZXhhbXBsZSIsImsiOiJldmVudHMiLCJuIjoyMH0",
  "resume": {
    "feed": "events",
    "after_sequence": 20,
    "endpoint": "/v1/events?projectId=prj_example"
  }
}
```

| Field                                 | Type          | Presence | Meaning and constraints                                                                                                   |
| ------------------------------------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `v`                                   | number        | Required | See the typed schema and response example for this field. must equal 1                                                    |
| `kind`                                | string        | Required | See the typed schema and response example for this field. must equal "history\_gap"                                       |
| `type`                                | string        | Required | See the typed schema and response example for this field. must equal "webhook.history\_gap"                               |
| `code`                                | string        | Required | See the typed schema and response example for this field. must equal "CURSOR\_EXPIRED"                                    |
| `id`                                  | string        | Required | See the typed schema and response example for this field.                                                                 |
| `feed`                                | string        | Required | See the typed schema and response example for this field. must equal "events"                                             |
| `workspace_id`                        | string        | Required | Identifier returned by the related operation.                                                                             |
| `project_id`                          | string / null | Required | Identifier returned by the related operation.                                                                             |
| `created_at`                          | string        | Required | See the typed schema and response example for this field. pattern: "^\d\{4}-\d\{2}-\d\{2}T.\*(?:Z\|\[+-]\d\{2}:\d\{2})\$" |
| `computed`                            | boolean       | Required | See the typed schema and response example for this field. must equal false                                                |
| `totals`                              | null          | Required | See the typed schema and response example for this field.                                                                 |
| `resync_required`                     | boolean       | Required | See the typed schema and response example for this field. must equal true                                                 |
| `history_gap`                         | object        | Required | See the typed schema and response example for this field. additional fields rejected                                      |
| `history_gap.after_sequence`          | integer       | Required | See the typed schema and response example for this field. minimum: 0                                                      |
| `history_gap.through_sequence`        | integer       | Required | See the typed schema and response example for this field. minimum: 0                                                      |
| `history_gap.scope`                   | string        | Required | See the typed schema and response example for this field. must equal "workspace\_feed"                                    |
| `history_gap.reason`                  | string        | Required | See the typed schema and response example for this field. must equal "history\_unavailable"                               |
| `history_gap.affected_project_events` | string        | Required | See the typed schema and response example for this field. must equal "unknown"                                            |
| `snapshot_endpoint`                   | string        | Required | See the typed schema and response example for this field. pattern: "^/v1/exports/watches(?:\\?projectId=\[^#]\*)?\$"      |
| `resume_cursor`                       | string        | Required | See the typed schema and response example for this field. pattern: "^\[A-Za-z0-9\_-]+\$"                                  |
| `resume`                              | object        | Required | See the typed schema and response example for this field. additional fields rejected                                      |
| `resume.feed`                         | string        | Required | See the typed schema and response example for this field. must equal "events"                                             |
| `resume.after_sequence`               | integer       | Required | See the typed schema and response example for this field. minimum: 0                                                      |
| `resume.endpoint`                     | string        | Required | See the typed schema and response example for this field. pattern: "^/v1/events(?:\\?projectId=\[^#]\*)?\$"               |
| `delivery_mode`                       | string        | Optional | See the typed schema and response example for this field. must equal "digest"                                             |
| `window`                              | object        | Optional | See the typed schema and response example for this field. additional fields rejected                                      |
| `window.opened_at`                    | string        | Required | See the typed schema and response example for this field. pattern: "^\d\{4}-\d\{2}-\d\{2}T.\*(?:Z\|\[+-]\d\{2}:\d\{2})\$" |
| `window.closed_at`                    | string        | Required | See the typed schema and response example for this field. pattern: "^\d\{4}-\d\{2}-\d\{2}T.\*(?:Z\|\[+-]\d\{2}:\d\{2})\$" |
| `feed`                                | string        | Required | See the typed schema and response example for this field. must equal "target\_events"                                     |
| `snapshot_endpoint`                   | string        | Required | See the typed schema and response example for this field. pattern: "^/v1/targets(?:\\?projectId=\[^#]\*)?\$"              |
| `resume.feed`                         | string        | Required | See the typed schema and response example for this field. must equal "target\_events"                                     |
| `resume.endpoint`                     | string        | Required | See the typed schema and response example for this field. pattern: "^/v1/target-events(?:\\?projectId=\[^#]\*)?\$"        |
