# Webhooks

Verify receivers and notify them of terminal approval requests.

## Verify a proposed receiver

`POST Configured receiver URL`

Sent to the exact configured URL before creating an instance or replacing
its delivery configuration. Signed with the proposed secret. The receiver
must verify the signature and expected registration before echoing the
challenge. Only a 200 response with the matching challenge within 10
seconds succeeds. A bare 2xx response is insufficient. Redirects are not
followed. One attempt per provisioning or update attempt; this is not
retried on the notification schedule. Failures leave no active new
configuration. Registration retries must be rate limited and coalesced.
The instance ID may be reserved before creation, but is not yet usable.
registration_id echoes delivery.registration_id from the proposed
configuration. The receiver uses it to select a preconfigured secret,
then verifies the original body bytes before trusting the instance ID
or answering the challenge. Unknown registrations must be rejected.


Authentication: Webhook Signature

- `webhook-id` (required): The message ID, equal to body.id. Stable across notification retries.

- `webhook-timestamp` (required): Delivery attempt time as Unix seconds, as required by Standard Webhooks.

### Request: challenge

```json
{
  "id": "c9bb5281-f9a5-4660-b27e-0b1cbbad81b6",
  "type": "webhook.verification",
  "registration_id": "8246931c-4ce0-4c15-a94a-e9c078ad06e2",
  "instance_id": "b29a43a7-1949-4b9f-9d61-b7ae66f85d31",
  "created_at": "2026-09-16T11:59:00Z",
  "challenge": "2edc34d7-cc57-44b1-8705-2fa608d3224a"
}
```

### Response 200

The receiver accepts this registration and echoes its challenge.

```json
{
  "challenge": "2edc34d7-cc57-44b1-8705-2fa608d3224a"
}
```

### Response 400

The verification message is malformed.

### Response 401

The signature or delivery timestamp is invalid.

### Response 403

The receiver does not accept this registration.

### Response 429

The receiver is rate limited; verification fails for this attempt.

See [Schemas](/specification/reference/schemas) and [OpenAPI](/specification/reference/openapi) for all fields and constraints.

## Notify a receiver of a terminal request

`POST Configured receiver URL`

Sent to the instance's current verified URL. This is a notification only;
retrieve the decision through the authenticated approval API. The message
ID and body stay fixed across retries and receiver updates. Each attempt
has a fresh delivery timestamp and signature. webhook-id equals body.id.

Any 2xx acknowledges durable receipt, not execution. The receiver must
verify the signature, save or queue the notification, and deduplicate
before arranging resumption. Duplicates already saved receive 2xx too.
The provider retries failures with exponential backoff and jitter until
acknowledgement or seven days (168 hours) after decision.decided_at.
There must be at most one outstanding attempt per event.
Each attempt has a timeout of at most 10 seconds and must end by that
deadline. Respect Retry-After on 429 and 503 within the retry window.
Do not follow redirects. Removal of delivery or deletion of the instance
ends retries. Approval expiry does not end notification delivery.

Retain the request, creation idempotency key and notification until the
later of seven days after the decision or 24 hours after delivery ends.
If delivery is removed whilst an attempt is in flight, the grace period
starts after that attempt finishes. Receivers retain duplicate tracking
through the retry window and grace period; eight days after event creation
covers both. Retention does not preserve access for revoked credentials.
Replacing the receiver does not restart the delivery window.


Authentication: Webhook Signature

- `webhook-id` (required): The message ID, equal to body.id. Stable across notification retries.

- `webhook-timestamp` (required): Delivery attempt time as Unix seconds, as required by Standard Webhooks.

### Request: resolved

```json
{
  "id": "f4b96ac4-404d-4f6a-9a03-a0565edc4922",
  "type": "request.resolved",
  "instance_id": "b29a43a7-1949-4b9f-9d61-b7ae66f85d31",
  "request_id": "7ab8c8ec-7b2d-4fd6-9b52-752f9515eb71",
  "created_at": "2026-09-16T12:02:00Z"
}
```

### Response 400

The notification is malformed.

### Response 401

The signature or delivery timestamp is invalid.

### Response 429

Reduce delivery rate to this destination and respect Retry-After.

- `Retry-After`: The delay before retrying, in seconds or as an HTTP date.

### Response 503

The receiver is temporarily unavailable. Respect Retry-After.

- `Retry-After`: The delay before retrying, in seconds or as an HTTP date.

### Response 2XX

Signature verified and receipt durably recorded. Any response body is ignored.

See [Schemas](/specification/reference/schemas) and [OpenAPI](/specification/reference/openapi) for all fields and constraints.