Prefer Swagger UI? Click hereThe same API in the classic OpenAPI explorer.
POST/v1/webhooks

Webhooks

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

NameTypeDefaultDescription
url*stringReceiver 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_minutesintegeroptionalHow long the previous secret stays valid after a rotate, so in-flight deliveries verify during cutover.

* required.

Query parameters

NameTypeDefaultDescription
statusstringoptionalOn GET /v1/webhooks: filter by active | paused | disabled_after_failures.

Headers

HeaderDirDescription
X-FC-Signature← resOn 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

NameTypeDescription
idstringWebhook endpoint id (wh_*).
urlstringThe receiver URL.
eventsstring[]Subscribed event types.
statusstringactive | paused | disabled_after_failures.
secretstringHMAC signing secret. Returned ONLY on create and on rotate_secret; store it now.
failure_streakintegerConsecutive delivery failures; the endpoint auto-disables past the threshold.
rotation_grace_minutesintegerGrace window for the previous secret on rotate.
previous_secret_expires_atstring | nullISO 8601 when the old secret stops verifying (during a rotation grace).
last_success_atstring | nullISO 8601 of the last 2xx delivery.
last_failure_atstring | nullISO 8601 of the last failed delivery.
created_atstringISO 8601.
updated_atstringISO 8601.

Errors

  • 401missing_tokenNo Authorization header was sent.
  • 401invalid_auth_schemeThe scheme was neither Bearer nor Client.
  • 401invalid_token_formatA Bearer token not prefixed ko_.
  • 401invalid_tokenThe API key is unknown, revoked or expired.
  • 403insufficient_scopeThe key is valid but lacks the required scope.
  • 400malformed_jsonBody is not valid JSON.
  • 400invalid_urlurl is missing or empty.
  • 400insecure_webhook_urlurl is not https.
  • 400malformed_webhook_urlurl is not a parseable URL or resolves to a private/SSRF-unsafe host.
  • 400empty_eventsevents is missing or an empty array.
  • 400unknown_event_typeevents contains a type Ringside does not emit.
  • 400invalid_rotation_gracerotation_grace_minutes is out of range.
  • 409webhook_url_in_useYou already registered this url.
  • 400invalid_status_transitionPATCH status must move active <-> paused (disabled_after_failures is worker-only).
  • 404not_foundNo such endpoint (also cross-tenant).

See the full error reference.

Related routes

GET/v1/webhooksList 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_secretMint a new secret; the old one verifies through the grace window.
POST/v1/webhooks/{id}/testSend a webhook.test event to the endpoint now.
GET/v1/webhooks/{id}/deliveriesList recent delivery attempts + their response status.
POST/v1/webhooks/{id}/deliveries/{delivery_id}/retryReplay 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).

Examples