Webhooks

Chab supports persistent Pro/Scale webhook endpoint aggregates and one-off operation callbacks. Endpoints created through /v1 bind to their creating token and use api:webhooks:read and api:webhooks:write.

Team admins can also manage endpoints in the signed-in dashboard after a recent password confirmation. A dashboard endpoint can be bound to a specific team API key or to an admin user. A key-bound endpoint follows that exact key's current permissions; another key on the same team cannot inherit it. Changing the endpoint to a user binding removes the old key binding.

GET/v1/webhooks/endpoints

Lists endpoint aggregates owned by the current token.

POST/v1/webhooks/endpoints

Creates an HTTPS endpoint. The signing secret is returned once.

Subscribe to any of operation.succeeded, operation.failed, operation.cancelled, operation.timed_out, operation.expired, credit.low, or credit.exhausted. Operation filters support projects, operation_keys, and families; on a mixed subscription those filters apply only to operation events.

Pro supports 3 enabled endpoints per team, up to 10 project filters and 25 operation-key filters per endpoint. Scale supports 10 endpoints, 100 project filters, and 100 operation-key filters. Both support all 7 event types, up to 10 family filters, and a 64 KiB encoded envelope. Creating or re-enabling an endpoint checks the enabled-team limit atomically.

GET/v1/webhooks/endpoints/{endpoint_id}

Returns one current-token endpoint without its secret.

PATCH/v1/webhooks/endpoints/{endpoint_id}

Updates URL, events, filters, or enabled state.

DELETE/v1/webhooks/endpoints/{endpoint_id}

Disables delivery and permanently prevents replay. Queued attempts are blocked; secret destruction waits for active delivery leases. Safe endpoint and delivery history remains for 30 days on Pro and 90 days on Scale.

POST/v1/webhooks/endpoints/{endpoint_id}/rotate-secret

Returns generation N+1 once; N overlaps for 15 minutes.

GET/v1/webhooks/deliveries

Lists delivery attempts for current-token endpoints.

GET/v1/webhooks/deliveries/{delivery_id}

Returns safe attempt status and retry timing.

POST/v1/webhooks/deliveries/{delivery_id}/replay

Creates one replay after current authorization is checked.

POST/v1/webhooks/replays

Replays a bounded endpoint/event/time window.

Replay event types must still be subscribed by the endpoint. Pro windows span at most 7 days and 500 events; Scale windows span at most 30 days and 5,000 events. Only events whose retention timestamp is still in the future are replayable; a delayed cleanup run does not extend that window. Current endpoint ownership, event subscription, originating token scope, project and connected- data authority, and filters are checked again before delivery.

One-Off Operation Callbacks

curl https://www.chab.ai/v1/webhooks/endpoints
curl -X POST https://www.chab.ai/v1/webhooks/endpoints
curl https://www.chab.ai/v1/webhooks/endpoints/whe_example
curl -X PATCH https://www.chab.ai/v1/webhooks/endpoints/whe_example
curl -X DELETE https://www.chab.ai/v1/webhooks/endpoints/whe_example
curl -X POST https://www.chab.ai/v1/webhooks/endpoints/whe_example/rotate-secret
curl https://www.chab.ai/v1/webhooks/deliveries
curl https://www.chab.ai/v1/webhooks/deliveries/del_example
curl -X POST https://www.chab.ai/v1/webhooks/deliveries/del_example/replay
curl -X POST https://www.chab.ai/v1/webhooks/replays

For an operation-creating request, include top-level callback fields:

{
  "simulate_async": true,
  "callback_url": "https://example.com/chab-callback",
  "callback_secret": "replace-with-a-long-random-secret",
  "callback_events": ["operation.succeeded"],
  "callback_notify_on_expiration": true,
  "data": {
    "hello": "world"
  }
}

callback_url must be HTTPS, must not contain credentials or secret-bearing query parameters, and must resolve to a public address. callback_secret is required with callback_url and is used only for signing that operation's callback deliveries. To rotate the secret, create a new operation with a new secret.

callback_events is optional. When omitted, Chab sends callbacks for:

  • operation.succeeded
  • operation.failed
  • operation.cancelled
  • operation.timed_out

Set callback_notify_on_expiration=true to also receive operation.expired when retained result availability expires.

Free promotional operation starts cannot include one-off callback fields. Omit callback_url, callback_secret, callback_events, and callback_notify_on_expiration and poll the operation status/result URLs instead. Chab returns validation_failed before storing a callback secret, creating an operation, holding credits, or dispatching provider work. Free promotional credits are one-off grants and do not refresh monthly.

Envelope

Callbacks use the webhook-event-envelope-v1 shape:

{
  "id": "evt_123",
  "type": "operation.succeeded",
  "version": "2026-08-16",
  "created_at": "2026-06-27T12:00:00+00:00",
  "team_id": 123,
  "request_id": "req_123",
  "operation_id": "op_01JZ7H0A1B2C3D4E5F6G7H8J9K",
  "project_id": null,
  "connection_id": null,
  "data": {
    "operation": {
      "id": "op_01JZ7H0A1B2C3D4E5F6G7H8J9K",
      "status": "succeeded",
      "operation_key": "examples.echo",
      "family": "examples",
      "result_available": true,
      "result_url": "/v1/operations/op_01JZ7H0A1B2C3D4E5F6G7H8J9K/result",
      "public_error_code": null,
      "warnings": [],
      "diagnostic_reference": null
    },
    "settlement": null
  },
  "meta": {
    "attempt": 1,
    "attempted_at": "2026-08-16T12:00:01+00:00",
    "replay": false,
    "api_version": "v1"
  }
}

The payload is provider-neutral. It never includes provider task IDs, raw provider responses, callback secrets, or private diagnostic details. Research callbacks use this same operation envelope; fetch the retained operation result to read reports, sources, citations, warnings, and artifacts.

Credit events use the same outer fields but contain only a threshold category (low or exhausted) and crossing time. They never include balance, purchase, package, ledger, or provider data. Chab pins logical event ID, version, created_at, and data when the source transaction commits; retries and replays reuse that snapshot.

Signing

Each delivery includes:

Chab-Event-Id: evt_123
Chab-Delivery-Id: del_attempt_123
Chab-Timestamp: 1782561600
Chab-Signature: v1=<hex-hmac>
Chab-Signature-Key-Version: 1

Compute the expected signature as HMAC-SHA256 over:

{Chab-Timestamp}.{raw_request_body}

Use the operation's callback_secret for a one-off callback or the endpoint's one-time signing secret for a persistent subscription. Compare the hex digest to the v1= value with a constant-time comparison and reject timestamps outside a 5 minute tolerance.

Use Chab-Event-Id as the idempotency key in your receiver. Retries use the same event id and a new Chab-Delivery-Id for each attempt. Future replay deliveries use the same event id with meta.replay=true. meta.attempted_at records the server time for the individual network attempt; the event's outer created_at remains unchanged.

Delivery Retries

Persistent delivery uses a 10-second request timeout and at most five attempts: the initial attempt, then retries near 30, 90, 270, and 810 seconds with stable endpoint/event jitter of at most 15%. Non-2xx responses and transport failures retry as a new delivery record and ID; the failed record retains its status and next-attempt time. Receiver content is discarded after a redacted 2 KiB diagnostic preview; exhaustion becomes dead_lettered. Delivery is at least once and has no global, team, endpoint, or cross-event ordering guarantee.

A failed webhook never changes operation status, result, or credit settlement. Treat delivery as notification only and poll operation status when the receiver needs to confirm current state.

For a connection-bound operation, Chab rechecks the creating API key, operation scope, project grant, connection state, and exact capability immediately before every initial or replay callback network attempt. If access was removed, no HTTP request is made. The delivery is recorded as blocked with connection_access_removed, and the operation records the same terminal error. This cannot undo provider work that was already dispatched, so recorded incurred work can still settle.

Persistent token-bound endpoints apply the equivalent current token, scope, operation-owner, and project checks to every attempt. Credit delivery also requires current api:credits:read. Authorization removal creates a terminal blocked attempt without DNS or HTTP work; an explicit replay can succeed only after current authorization is restored.