202, the object was not immediately readable, and the thing that establishes delivery is a signed webhook that arrives minutes later.
Webhook receipt and signature verification are gateway-owned (ADR-0021). Native vendor webhooks terminate at the gateway, not at the control plane, because the shared webhook secret is encrypted per-subscription connection material and plaintext exists only inside the gateway process.
The ingress
Endpoints
1
Resolve the subscription
The workspace, connection, and subscription come from the path. An unknown subscription is
404. A subscription whose signature profile does not match the connection’s tier, custody, and connector slug is refused — a Nango or Composio profile requires federated custody with a broker reference, and every other profile requires a native, Obol-custody connection.2
Rate limit
Known-subscription traffic is capped at 120 requests per minute per workspace, connection, and subscription. Over that is
429.3
Check the registered path
If the subscription pins a
notification_url, the request path must match it. That URL is configuration and is never derived from Host or forwarded headers.4
Bound the body
Bodies are limited to 256 KiB. Larger is
413.5
Verify the signature over the original bytes
The gateway HMACs the raw body — before parsing it — against the current key and, inside the rotation overlap, the previous key. Comparison is constant-time. A profile that signs a timestamp has it authenticated and bounded in both directions against the subscription’s declared tolerance.
6
Check replay identity, then parse
Deduplication is workspace + connection + subscription + provider event id. A repeated id carrying a different body digest is a
409 and a security log line, never evidence.Signature profiles
SignatureProfile is a closed, reviewed registry — never a config language, never a regex, never a per-vendor plugin. Adding one is a reviewed, security-sensitive change. stripe_v1 (t=…,v1=… over t.body) and github_sha256 (X-Hub-Signature-256 over the raw body) are the two called out by name in the contract; the registry also covers Svix-shaped and standard-webhooks-shaped schemes, and a broad set of per-vendor HMAC variants.
Trust follows the profile:
nango_sha256andcomposio_sha256establishbroker_attested.- Every other reviewed profile establishes
vendor_signed.
vendor_signed.
The GitHub caveat
GitHub authenticates neither the event-family header, nor the delivery id, nor a timestamp. So for that profile the gateway derives the supported event family from the signed payload structure, rejects a conflicting unsigned family header, requires a UUID-shaped delivery id, and deduplicates the signed body digest as well as the provider id. This still does not prove freshness of a previously unseen GitHub payload, and no upstream correlation id is inferred from an ambiguous shared payload shape. Recording that limit is the point; papering over it would let a captured review event be relabeled as a pull request event and bypass the declared family.Evidence before correlation
A valid delivery becomes an immutable, versionedWebhookEvidenceEvent — distinct from the invocation-correlated VerificationEvent. It carries an evidence id, subscription and event type, safe correlation ids, trust, signature profile and key version, a body digest, an authenticated array position for providers that batch, and bounded assertion outcomes. It carries no raw body.
Webhook evidence event
Provisioning a subscription
Signing material reaches the control plane already encrypted. Control never sees plaintext.1
Seal the secret locally
A local gateway command reads the secret from non-interactive stdin — never a terminal, never an argument — and seals it under the same KEK as the target gateway. It starts no service; the only potentially remote work is configured KMS access.The secret must be 16 to 4096 bytes, and control characters are rejected except inside a PEM public-key block.
Seal a webhook signing key
--valid-for-days accepts 1 through 30 and defaults to 7. Output is the standard encrypted signing-key contract, including its masked last4.2
Register it on the connection
Provision a subscription
3
Rotate before expiry
A replacement must be new secret material under a new version. Re-encrypting the same plaintext, reintroducing a key that has left the retained current/previous pair, or recreating it under a new subscription id is not cryptographically detected, so treat rotation as an operational discipline. Previous-key overlap is capped at 24 hours, replay markers are retained for 30 days, and
DELETE on the same path removes the subscription.GET on the connection’s webhooks path. See Webhooks API.
Recovering receipt delivery
Signed evidence is only useful if the resulting receipt revisions actually reach the audit projection. The gateway’s audit stream is delivered at least once, and a consumer can fail after receiving an entry and before committing it. So the control consumer reclaims pending deliveries with boundedXAUTOCLAIM pages and persists the returned scan position under a workspace-, group-, and consumer-qualified Redis key, rather than restarting the scan from zero and stranding eligible entries behind a long prefix of recently renewed leases. A drain admits no more than its batch limit across reclaimed and fresh entries, commits before acknowledging, and rolls back one workspace’s transaction without blocking another’s (ADR-0042).
Each receipt is projected with a single conditional upsert that requires the incoming revision to be strictly greater than the stored one and the workspace to match, so overlapping consumers cannot write a stale revision over a newer one. Exactly-once transport is not claimed; monotonic projection is what makes at-least-once safe.
Listing endpoints page with a versioned opaque cursor over (occurred_at, invocation_id) descending, so ordinary traffic inside a boundary second is traversable. Legacy RFC3339 before values are still accepted as timestamp bounds, and a malformed cursor is rejected rather than silently substituted with the current time.
Related
Verification
How correlated webhook evidence advances a verification plan.
Audit log
Where projected receipts and revisions are queried.