Create a webhook subscription
POST
/api/v1/webhook_subscriptions
webhooks:write
Returns the signing_secret EXACTLY ONCE. Store it; subsequent
reads never re-expose it (use rotateWebhookSecret if you lose it).
The URL is validated against the SSRF guard (no private / loopback /
cloud-metadata addresses, no blocked service ports). An empty or
omitted event_filter subscribes to every event. Requires
webhooks:write.
Request body
The endpoint URL and the events to deliver to it.
-
urlstring (uri) required HTTPS endpoint. Must resolve to a public address (no private, loopback or cloud-metadata ranges). -
event_filterarray of WebhookEventName Events to receive. Omit or pass `[]` for all events; unknown names return `422`. -
activeboolean
Example request
{
"url": "https://hooks.partner.example/cascades",
"event_filter": [
"execution.completed",
"execution.failed",
"adverse_action.completed"
]
}Responses
-
201 Created (response includes signing_secret)
application/json-
dataWebhookSubscriptionSchema: WebhookSubscriptionWithSecret
-
idinteger -
objectstring -
urlstring -
event_filterarray of WebhookEventName Empty = subscribed to ALL events. -
activeboolean -
last_delivered_atstring (date-time), nullable -
created_atstring (date-time) -
updated_atstring (date-time) -
signing_secretstring
-
-
metaMetaSchema: Meta
-
request_idstring required Correlation id for this request. Also sent as the `X-Request-Id` response header; quote it when contacting support.
-
201 example{ "data": { "id": 9, "object": "webhook_subscription", "url": "https://hooks.partner.example/cascades", "event_filter": [ "execution.completed", "execution.failed", "adverse_action.completed" ], "active": true, "last_delivered_at": null, "created_at": "2026-09-09T13:00:00Z", "updated_at": "2026-09-09T13:00:00Z", "signing_secret": "whsec_4f1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9" }, "meta": { "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f" } } -
-
401 Missing, malformed, expired or revoked bearer token. The
WWW-Authenticateheader carries the RFC 6750 challenge.application/json401 example{ "error": { "code": "unauthorized", "message": "A valid bearer token is required.", "details": [], "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f", "doc_url": "https://cascades.crscreditapi.com/docs/api/errors/unauthorized" } } -
403 Token does not hold the required scope.
detailslists the scope(s) that were checked.application/json403 example{ "error": { "code": "insufficient_scope", "message": "This token does not have the required scope.", "details": [ { "field": "scope", "message": "executions:write" } ], "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f", "doc_url": "https://cascades.crscreditapi.com/docs/api/errors/insufficient_scope" } } -
422
validation_failed— bad URL, non-public host, or an unknown event nameapplication/json422 example{ "error": { "code": "validation_failed", "message": "The request could not be processed.", "details": [ { "field": "event_filter", "message": "contains unknown event: execution.started" } ], "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f", "doc_url": "https://cascades.crscreditapi.com/docs/api/errors/validation_failed" } } -
429 Rate limit exceeded (60 req/min per IP on
/api/v1, 600 req/hour per token prefix, or 300 req/5 min per IP globally). The body is the standard error envelope andRetry-Aftergives the seconds until the window resets.application/json429 example{ "error": { "code": "rate_limited", "message": "Rate limit exceeded. Retry later.", "details": [], "request_id": "7d4c2f48-9a1b-4c3d-8e2f-1a2b3c4d5e6f", "doc_url": "https://cascades.crscreditapi.com/docs/api/errors/rate_limited" } }