# WebhookSubscription

object type

A WebhookSubscription — the documented non-GraphQL edge OUT (mirrors the inbound Stripe webhook edge): the merchant registers an https target URL + a filtered set event names (the DERIVED deliverable universe — transition success events + per-construct birth names; enumerate it via the integrator docs), and the delivery lane POSTs HMAC-signed THIN payloads at-least-once with retries/backoff/DLQ. The payload names {id, event, occurredAt, construct{type,id}, revision, subscriptionId} — the receiver dedupes on id and READS the record through this SAME API with its ApiKey (no fat payloads, no parallel surface). Signing mirrors the Stripe scheme (almondtill-signature: t=<unix>,v1=<hex HMAC-SHA256 of <t>.<body> under the secret>); the secret shows ONCE at create/rotate and lives in the non-streamed credential store, never on the record. GROUP-parented (parentId === rootId); born active; 20 consecutive delivery failures fire the automatic active → suspended flip (system:repeated_delivery_failure — the deliverer, never a caller); reactivate serves both inactive → active and suspended → active (fix the endpoint, then reactivate; missed events do NOT replay — re-sync via the API). Owner-gated end to end (the mintApiKey posture); NOT searchable (integration config — reached via the owner-gated list).

## Fields

| Field | Type | Notes |
| --- | --- | --- |
| `id` | [ID](/types/#scalars) `ID!` | The record’s id — a UUID the platform assigned when the record was created; every reference to this record uses it. |
| `sysId` | [String](/types/#scalars) `String!` | The group-scoped human-facing system id (WH-…). |
| `type` | [String](/types/#scalars) `String!` | The kind of record — always `WebhookSubscription` here. |
| `caption` | [String](/types/#scalars) `String!` | The record’s display name — what people see it called. |
| `status` | [String](/types/#scalars) `String!` | The FSM state: active \| inactive \| suspended \| doomed. |
| `parentId` | [ID](/types/#scalars) `ID!` | The parent = the org GROUP (the parent-is-the-org-group clause); parentId === rootId always (the Affiliate class — deliveries span the whole group’s orgs). |
| `rootId` | [ID](/types/#scalars) `ID!` | The org-group family root. |
| `createdAt` | [String](/types/#scalars) `String!` | When the record was created, as a UTC timestamp. |
| `updatedAt` | [String](/types/#scalars) `String!` | When the record last changed, as a UTC timestamp. |
| `revisionNum` | [Int](/types/#scalars) `Int!` | How many times this record has been edited; the first save is 0. |
| `revision` | [ID](/types/#scalars) `ID!` | The OCC revision token — supply it on every mutation of this record; rotates on every write. |
| `refCaptions` | [RefCaption](/types/RefCaption/) `[RefCaption!]!` | The server-composed captions of this record's declared references (the referenced-caption rule) — one row per referenced id; see RefCaption. |
| `targetUrl` | [String](/types/#scalars) `String!` | The https delivery endpoint (the egress law: https only, no credentials, no explicit port, no IP-literal/localhost/private-suffix hosts; ≤512 chars). Mutable via update — fixing the endpoint is the suspended-repair flow. |
| `eventTypes` | [String](/types/#scalars) `[String!]!` | The event-name filter set (1..64, deduped) — each name must be in the DERIVED deliverable universe (transition success events + at.<domain>.<entity>.create.ok.v1 birth names over the whole construct catalog). |
| `consecutiveFailures` | [Int](/types/#scalars) `Int!` | Consecutive delivery-attempt failures (the deliverer’s counter — reset by the first success after failures; NEVER written on steady-state success). At 20 the automatic suspend fires. |
| `lastFailureAt` | [String](/types/#scalars) | The last failed delivery attempt’s instant (failure-path-only; stands as history after recovery until the next failure overwrites). |
| `lastFailureCode` | [String](/types/#scalars) | The last failure’s short token — an HTTP status (http_500) or a fetch-error class (timeout, network); never a response body. |
| `suspendedAt` | [String](/types/#scalars) | Stamped by the automatic suspend flip; cleared by reactivate. |
| `secretRotatedAt` | [String](/types/#scalars) | The last signing-secret rotation’s instant; null until the first rotation. |
| `pluginId` | [ID](/types/#scalars) | The owning Plugin (optional): this subscription is part of that plugin’s declared event feed; IMMUTABLE at birth (a re-bind = a new subscription); an un-doomed bound subscription blocks the plugin’s doom. |

## Used by

- [deactivateWebhookSubscription](/reference/webhook-subscription/deactivateWebhookSubscription/)
- [doomWebhookSubscription](/reference/webhook-subscription/doomWebhookSubscription/)
- [reactivateWebhookSubscription](/reference/webhook-subscription/reactivateWebhookSubscription/)
- [updateWebhookSubscription](/reference/webhook-subscription/updateWebhookSubscription/)
- [webhookSubscription](/reference/webhook-subscription/webhookSubscription/)
- [webhookSubscriptions](/reference/webhook-subscription/webhookSubscriptions/)
