POST
/v1/webhooksWebhooks
Register an HTTPS endpoint that Ringside POSTs signed events to: customer lifecycle, budget and rate-limit blocks, wallet.low/empty, moderation flags, batch lifecycle, and (as runs land) run.completed/failed/cancelled. Each delivery carries an X-FC-Signature HMAC header so you can verify authenticity. The signing secret is returned once on create; deliveries retry with backoff and an endpoint that keeps failing is auto-disabled.
Request
- HTTP
- POST
- URL
- /v1/webhooks
- Auth
- api_key
Try it
# register an endpoint (secret returned ONCE)
curl https://api.fightclub.pro/v1/webhooks \
-H "Authorization: Bearer $FC_API_KEY" -H "Content-Type: application/json" \
-d '{"url":"https://yourapp.com/wh/fc",
"events":["customer.budget_exceeded","wallet.low","batch.completed"]}'
# => { "id":"wh_...", "secret":"whsec_...", "status":"active", ... }
# fire a test event, then inspect deliveries
curl -X POST https://api.fightclub.pro/v1/webhooks/wh_abc/test \
-H "Authorization: Bearer $FC_API_KEY"
curl https://api.fightclub.pro/v1/webhooks/wh_abc/deliveries \
-H "Authorization: Bearer $FC_API_KEY"Example response
{
"id": "wh_7Bb0",
"url": "https://app.example.com/ringside/webhooks",
"events": [ "customer.budget_exceeded", "batch.completed" ],
"status": "active",
"secret": "whsec_...shown_once...",
"failure_streak": 0,
"rotation_grace_minutes": 60,
"previous_secret_expires_at": null,
"last_success_at": null,
"last_failure_at": null,
"created_at": "2026-06-24T12:00:00.000Z",
"updated_at": "2026-06-24T12:00:00.000Z"
}A representative 200 body. Ids and timestamps are illustrative.
Body parameters
| Name | Type | Default | Description |
|---|---|---|---|
| url* | string | — | Receiver URL. Must be https and must not resolve to a private/SSRF-unsafe host. Unique per developer. |
| events* | string[] | — | Non-empty array of event types to subscribe to (see the event list below). An unknown type is rejected with unknown_event_type. |
| rotation_grace_minutes | integer | optional | How long the previous secret stays valid after a rotate, so in-flight deliveries verify during cutover. |
* required.
Query parameters
| Name | Type | Default | Description |
|---|---|---|---|
| status | string | optional | On GET /v1/webhooks: filter by active | paused | disabled_after_failures. |
Headers
| Header | Dir | Description |
|---|---|---|
| X-FC-Signature | ← res | On the delivery POST to your endpoint: t=<unix_ts>,v1=<hex_hmac_sha256>. HMAC-SHA256 of the raw body keyed by your secret. During a rotation grace the header carries two v1= digests (current + previous); accept if either matches. |
Response fields
| Name | Type | Description |
|---|---|---|
| id | string | Webhook endpoint id (wh_*). |
| url | string | The receiver URL. |
| events | string[] | Subscribed event types. |
| status | string | active | paused | disabled_after_failures. |
| secret | string | HMAC signing secret. Returned ONLY on create and on rotate_secret; store it now. |
| failure_streak | integer | Consecutive delivery failures; the endpoint auto-disables past the threshold. |
| rotation_grace_minutes | integer | Grace window for the previous secret on rotate. |
| previous_secret_expires_at | string | null | ISO 8601 when the old secret stops verifying (during a rotation grace). |
| last_success_at | string | null | ISO 8601 of the last 2xx delivery. |
| last_failure_at | string | null | ISO 8601 of the last failed delivery. |
| created_at | string | ISO 8601. |
| updated_at | string | ISO 8601. |
Errors
- 401
missing_tokenNo Authorization header was sent. - 401
invalid_auth_schemeThe scheme was neither Bearer nor Client. - 401
invalid_token_formatA Bearer token not prefixed ko_. - 401
invalid_tokenThe API key is unknown, revoked or expired. - 403
insufficient_scopeThe key is valid but lacks the required scope. - 400
malformed_jsonBody is not valid JSON. - 400
invalid_urlurl is missing or empty. - 400
insecure_webhook_urlurl is not https. - 400
malformed_webhook_urlurl is not a parseable URL or resolves to a private/SSRF-unsafe host. - 400
empty_eventsevents is missing or an empty array. - 400
unknown_event_typeevents contains a type Ringside does not emit. - 400
invalid_rotation_gracerotation_grace_minutes is out of range. - 409
webhook_url_in_useYou already registered this url. - 400
invalid_status_transitionPATCH status must move active <-> paused (disabled_after_failures is worker-only). - 404
not_foundNo such endpoint (also cross-tenant).
See the full error reference.
Related routes
| GET | /v1/webhooks | List endpoints (secrets redacted). Query: status. |
| GET | /v1/webhooks/{id} | Retrieve one endpoint. |
| PATCH | /v1/webhooks/{id} | Update url, events, or status (active <-> paused). |
| DELETE | /v1/webhooks/{id} | Delete an endpoint (204 No Content). |
| POST | /v1/webhooks/{id}/rotate_secret | Mint a new secret; the old one verifies through the grace window. |
| POST | /v1/webhooks/{id}/test | Send a webhook.test event to the endpoint now. |
| GET | /v1/webhooks/{id}/deliveries | List recent delivery attempts + their response status. |
| POST | /v1/webhooks/{id}/deliveries/{delivery_id}/retry | Replay a single delivery. |
Notes
- ·Verify every delivery: recompute HMAC-SHA256 of the raw request body with your secret and constant-time compare against the v1= digest in X-FC-Signature. Reject if the t= timestamp is too old.
- ·Event types include: customer.created, customer.archived, customer.hard_deleted, customer.budget_exceeded, customer.rate_limit_exceeded, wallet.low, wallet.empty, moderation.flagged, request.failed_after_retry, chat.completion.finished, batch.created/completed/failed/cancelled, vector_store.* and webhook.test. run.completed/failed/cancelled are reserved and emit as Runs roll out.
- ·The secret is shown once. If you lose it, POST rotate_secret and update your verifier within the grace window.
- ·Failed deliveries retry with backoff; a sustained failure streak flips the endpoint to disabled_after_failures and you must re-enable it (PATCH status=active).