{"success":true,"events":[{"event":"ufc.event.started","game":"ufc","kind":"lifecycle","description":"A UFC card has started, with the public card in data.bouts.","whenFires":"At the opening bell of the first fight. Once per card.","status":"not_observed","statusNote":"You can subscribe, but none has been recorded. Do not rely on it.","typicalFrequency":"none recorded so far","last7Days":0,"lastSentAt":null,"latency":null,"minimumPlan":"pro","plans":["PRO","BUSINESS","ENTERPRISE","ONE_GAME_STARTER (own game)","BASIC"],"filters":["eventSlug","eventId","boutId","fighterSlug","fighterId","series"],"filtersNote":"A filter on a field this event does not carry stops every delivery of it, so only filter on the fields listed here.","payloadBytes":null,"notes":"data.boutId is the id on /ufc/events/{slug}/bouts and /ufc/bouts/{id}; data.eventSlug and data.fighters[].fighterSlug are the /ufc ids. data.dedupeKey never changes for one event: use it as your idempotency key. UFC includes DWCS and Road to UFC (data.series).","fields":[],"example":null,"exampleCapturedAt":null,"docsUrl":"https://citoapi.com/docs/webhooks/#ufc-event-started"},{"event":"ufc.fight.started","game":"ufc","kind":"lifecycle","description":"A fight has started: data.startedAt is the estimated bell.","whenFires":"At the opening bell, not the walkouts. Once per fight.","status":"not_observed","statusNote":"You can subscribe, but none has been recorded. Do not rely on it.","typicalFrequency":"none recorded so far","last7Days":0,"lastSentAt":null,"latency":null,"minimumPlan":"pro","plans":["PRO","BUSINESS","ENTERPRISE","ONE_GAME_STARTER (own game)","BASIC"],"filters":["eventSlug","eventId","boutId","fighterSlug","fighterId","series"],"filtersNote":"A filter on a field this event does not carry stops every delivery of it, so only filter on the fields listed here.","payloadBytes":null,"notes":"data.boutId is the id on /ufc/events/{slug}/bouts and /ufc/bouts/{id}; data.eventSlug and data.fighters[].fighterSlug are the /ufc ids. data.dedupeKey never changes for one event: use it as your idempotency key. UFC includes DWCS and Road to UFC (data.series).","fields":[],"example":null,"exampleCapturedAt":null,"docsUrl":"https://citoapi.com/docs/webhooks/#ufc-fight-started"},{"event":"ufc.round.started","game":"ufc","kind":"live_tick","description":"A round has started: data.round.","whenFires":"When the clock of that round is first seen. Once per round.","status":"not_observed","statusNote":"You can subscribe, but none has been recorded. Do not rely on it.","typicalFrequency":"none recorded so far","last7Days":0,"lastSentAt":null,"latency":null,"minimumPlan":"pro","plans":["PRO","BUSINESS","ENTERPRISE"],"filters":["eventSlug","eventId","boutId","fighterSlug","fighterId","series"],"filtersNote":"A filter on a field this event does not carry stops every delivery of it, so only filter on the fields listed here.","payloadBytes":null,"notes":"data.boutId is the id on /ufc/events/{slug}/bouts and /ufc/bouts/{id}; data.eventSlug and data.fighters[].fighterSlug are the /ufc ids. data.dedupeKey never changes for one event: use it as your idempotency key. UFC includes DWCS and Road to UFC (data.series).","fields":[],"example":null,"exampleCapturedAt":null,"docsUrl":"https://citoapi.com/docs/webhooks/#ufc-round-started"},{"event":"ufc.round.completed","game":"ufc","kind":"live_tick","description":"A round has ended: data.round, data.endedBy (bell or finish), and each fighter's provisional round numbers in data.stats.","whenFires":"At the bell, or when the fight ends in that round. Once per round.","status":"not_observed","statusNote":"You can subscribe, but none has been recorded. Do not rely on it.","typicalFrequency":"none recorded so far","last7Days":0,"lastSentAt":null,"latency":null,"minimumPlan":"pro","plans":["PRO","BUSINESS","ENTERPRISE"],"filters":["eventSlug","eventId","boutId","fighterSlug","fighterId","series"],"filtersNote":"A filter on a field this event does not carry stops every delivery of it, so only filter on the fields listed here.","payloadBytes":null,"notes":"data.boutId is the id on /ufc/events/{slug}/bouts and /ufc/bouts/{id}; data.eventSlug and data.fighters[].fighterSlug are the /ufc ids. data.dedupeKey never changes for one event: use it as your idempotency key. UFC includes DWCS and Road to UFC (data.series).","fields":[],"example":null,"exampleCapturedAt":null,"docsUrl":"https://citoapi.com/docs/webhooks/#ufc-round-completed"},{"event":"ufc.fight.completed","game":"ufc","kind":"lifecycle","description":"A fight result: data.outcome (win, draw or no_contest), data.winner, data.loser, data.method, data.methodDetails, data.resultRound, data.resultTime.","whenFires":"When the result is held. Once per fight.","status":"not_observed","statusNote":"You can subscribe, but none has been recorded. Do not rely on it.","typicalFrequency":"none recorded so far","last7Days":0,"lastSentAt":null,"latency":null,"minimumPlan":"pro","plans":["PRO","BUSINESS","ENTERPRISE","ONE_GAME_STARTER (own game)","BASIC"],"filters":["eventSlug","eventId","boutId","fighterSlug","fighterId","series"],"filtersNote":"A filter on a field this event does not carry stops every delivery of it, so only filter on the fields listed here.","payloadBytes":null,"notes":"data.boutId is the id on /ufc/events/{slug}/bouts and /ufc/bouts/{id}; data.eventSlug and data.fighters[].fighterSlug are the /ufc ids. data.dedupeKey never changes for one event: use it as your idempotency key. UFC includes DWCS and Road to UFC (data.series).","fields":[],"example":null,"exampleCapturedAt":null,"docsUrl":"https://citoapi.com/docs/webhooks/#ufc-fight-completed"},{"event":"ufc.fight.stats_ready","game":"ufc","kind":"lifecycle","description":"The official whole-fight and per-round stats of a finished fight are available, in data.boutStats and data.roundStats, with the final result.","whenFires":"When both fighters have stats for every round fought. Once per fight.","status":"not_observed","statusNote":"You can subscribe, but none has been recorded. Do not rely on it.","typicalFrequency":"none recorded so far","last7Days":0,"lastSentAt":null,"latency":null,"minimumPlan":"pro","plans":["PRO","BUSINESS","ENTERPRISE","ONE_GAME_STARTER (own game)","BASIC"],"filters":["eventSlug","eventId","boutId","fighterSlug","fighterId","series"],"filtersNote":"A filter on a field this event does not carry stops every delivery of it, so only filter on the fields listed here.","payloadBytes":null,"notes":"data.boutId is the id on /ufc/events/{slug}/bouts and /ufc/bouts/{id}; data.eventSlug and data.fighters[].fighterSlug are the /ufc ids. data.dedupeKey never changes for one event: use it as your idempotency key. UFC includes DWCS and Road to UFC (data.series).","fields":[],"example":null,"exampleCapturedAt":null,"docsUrl":"https://citoapi.com/docs/webhooks/#ufc-fight-stats_ready"},{"event":"ufc.card.updated","game":"ufc","kind":"lifecycle","description":"A UFC card changed: data.changes lists bouts added, moved between sections or cancelled.","whenFires":"When a change on an upcoming or running card has held for 10 minutes.","status":"not_observed","statusNote":"You can subscribe, but none has been recorded. Do not rely on it.","typicalFrequency":"none recorded so far","last7Days":0,"lastSentAt":null,"latency":null,"minimumPlan":"pro","plans":["PRO","BUSINESS","ENTERPRISE","ONE_GAME_STARTER (own game)","BASIC"],"filters":["eventSlug","eventId","boutId","fighterSlug","fighterId","series"],"filtersNote":"A filter on a field this event does not carry stops every delivery of it, so only filter on the fields listed here.","payloadBytes":null,"notes":"data.boutId is the id on /ufc/events/{slug}/bouts and /ufc/bouts/{id}; data.eventSlug and data.fighters[].fighterSlug are the /ufc ids. data.dedupeKey never changes for one event: use it as your idempotency key. UFC includes DWCS and Road to UFC (data.series).","fields":[],"example":null,"exampleCapturedAt":null,"docsUrl":"https://citoapi.com/docs/webhooks/#ufc-card-updated"},{"event":"ufc.event.completed","game":"ufc","kind":"lifecycle","description":"A UFC card has finished, with every result in data.results.","whenFires":"When every public fight has a result. Once per card.","status":"not_observed","statusNote":"You can subscribe, but none has been recorded. Do not rely on it.","typicalFrequency":"none recorded so far","last7Days":0,"lastSentAt":null,"latency":null,"minimumPlan":"pro","plans":["PRO","BUSINESS","ENTERPRISE","ONE_GAME_STARTER (own game)","BASIC"],"filters":["eventSlug","eventId","boutId","fighterSlug","fighterId","series"],"filtersNote":"A filter on a field this event does not carry stops every delivery of it, so only filter on the fields listed here.","payloadBytes":null,"notes":"data.boutId is the id on /ufc/events/{slug}/bouts and /ufc/bouts/{id}; data.eventSlug and data.fighters[].fighterSlug are the /ufc ids. data.dedupeKey never changes for one event: use it as your idempotency key. UFC includes DWCS and Road to UFC (data.series).","fields":[],"example":null,"exampleCapturedAt":null,"docsUrl":"https://citoapi.com/docs/webhooks/#ufc-event-completed"}],"count":8,"games":["lol","cod","fortnite","cs2","dota2","tennis","ufc"],"plans":{"live_delivery":{"plans":["PRO","BUSINESS","ENTERPRISE"],"names":"Pro and Scale (and Enterprise)","pro_monthly_usd":59,"scale_monthly_usd":100},"closed_plans":"Accounts still on the closed One Game Starter and Basic plans keep webhooks for the events their plan allows. They cannot subscribe to the real-time CS2 events.","free_and_starter":"Free and Starter accounts can create a test inbox (POST /api/v1/webhook-inbox) and send sample events to it. Live delivery needs Pro or Scale.","upgrade_url":"https://citoapi.com/signup/pro?checkout=1&source=webhook_access","limits":{"webhooks_per_account":5}},"setup":[{"step":1,"title":"Pick events","detail":"GET /api/v1/webhooks/events lists every event, with status, volume, fields and a real example. Subscribe to the fewest that do the job: cs2.player.state_changed alone is hundreds of thousands of requests a week."},{"step":2,"title":"Get a URL","detail":"A public HTTPS route that accepts POST and answers 2xx within 10 seconds. No server yet? POST /api/v1/webhook-inbox gives you a hosted temporary URL, on any plan."},{"step":3,"title":"Create the webhook","detail":"POST /api/v1/webhooks with x-api-key and {\"url\":\"...\",\"events\":[\"...\"],\"filters\":{...}}. Needs Pro or Scale. Save webhook.secret from the response: it is shown once."},{"step":4,"title":"Verify signatures","detail":"HMAC-SHA256 of the raw request body with your secret, hex, compared with the X-Cito-Signature header. Hash the raw bytes before parsing JSON."},{"step":5,"title":"Send a test","detail":"POST /api/v1/webhooks/{id}/test with {\"event\":\"cs2.match.completed\"} sends a real recent sample, signed, marked as a test (X-Cito-Test: true, data.test true, test true). The response has the status code, latency and a plain-English diagnosis."},{"step":6,"title":"Watch deliveries","detail":"GET /api/v1/webhooks/{id}/deliveries lists each delivery. Handle retries with the payload id as your idempotency key."}],"envelope":{"fields":{"event":"The event name.","id":"Unique id of this event. Use it as your idempotency key (same as the X-Cito-Event-Id header).","timestamp":"When Cito sent this delivery, ISO 8601 UTC.","event_type":"The event name.","match_id":"The match id, or null when the event is not about one match.","previous_state":"The state before the change, on events that have one. Otherwise null.","data":"The event body. See fields and example on each event."},"test_markers":"A test delivery has test: true on the envelope, data.test true and the header X-Cito-Test: true. A replay has data.replayed true and data.original_event_id."},"signature_headers":["X-Cito-Signature","X-Cito-Event","X-Cito-Event-Id","X-Cito-Timestamp","X-Cito-Delivery-Attempt","X-Cito-Test (test deliveries only)"],"signing":{"algorithm":"HMAC-SHA256","signedContent":"UTF-8 bytes of the exact JSON request body sent by Cito","bodySerialization":"JSON.stringify(payload)","signatureHeader":"X-Cito-Signature","signatureEncoding":"hex","signaturePrefix":"none","secretFormat":"Use the exact webhook secret value shown when the webhook is created or regenerated. No whsec_ prefix is used.","verificationRule":"Verify against the raw request body bytes before reparsing or reserializing JSON.","sampleSecret":"cito_test_secret_123","samplePayload":{"event":"lol.live.state","id":"sample-event-id-123","timestamp":"2026-06-07T16:00:00.000Z","data":{"gameId":"lol-game-123","matchId":"lol-match-123","league":"LEC","gameTime":1337,"blueTeam":{"tag":"G2","kills":7,"gold":24800,"towers":3,"dragons":2,"barons":0},"redTeam":{"tag":"FNC","kills":5,"gold":23700,"towers":2,"dragons":1,"barons":0}}},"samplePayloadString":"{\"event\":\"lol.live.state\",\"id\":\"sample-event-id-123\",\"timestamp\":\"2026-06-07T16:00:00.000Z\",\"data\":{\"gameId\":\"lol-game-123\",\"matchId\":\"lol-match-123\",\"league\":\"LEC\",\"gameTime\":1337,\"blueTeam\":{\"tag\":\"G2\",\"kills\":7,\"gold\":24800,\"towers\":3,\"dragons\":2,\"barons\":0},\"redTeam\":{\"tag\":\"FNC\",\"kills\":5,\"gold\":23700,\"towers\":2,\"dragons\":1,\"barons\":0}}}","sampleSignature":"97db77f5cd96e09a70dcf6cc75fc5d92d780c41ce82c853b51e4070954c54171","sampleHeaders":{"Content-Type":"application/json","X-Cito-Signature":"97db77f5cd96e09a70dcf6cc75fc5d92d780c41ce82c853b51e4070954c54171","X-Cito-Event":"lol.live.state","X-Cito-Event-Id":"sample-event-id-123","X-Cito-Timestamp":"2026-06-07T16:00:00.000Z","X-Cito-Delivery-Attempt":"1"}},"delivery":{"timeout_ms":10000,"attempts_per_event":3,"retry_gaps_seconds":[1,2],"retried":"Timeouts, network errors, 5xx, 408 and 429.","not_retried":"Other 4xx answers.","success":"Any 2xx answer.","redirects":"307 and 308 keep the POST. 301, 302 and 303 turn it into a GET with no body, so set the webhook to the final URL.","ordering":"Not guaranteed. Use data timestamps or seq-like fields, and treat each event independently.","idempotency":"Use the payload id / X-Cito-Event-Id header as your idempotency key.","failure_pause":{"after_consecutive_failures":3,"pause_minutes":[1,5,15,30],"behaviour":"After 3 failed events in a row, Cito pauses sending to that webhook: 1, then 5, 15 and 30 minutes (30 is the cap). Events that occur during a pause are NOT queued for you. They are logged with status \"skipped_backoff\" and are not sent again unless you ask. Any success ends the pause.","after_recovery":"When the first delivery after a pause succeeds, Cito re-sends up to 50 missed match and result events from the last 24 hours, oldest first, with data.catch_up true and the same event id. POST /api/v1/webhooks/{id}/catch-up re-sends any still missing. POST /api/v1/webhooks/{id}/replay re-sends a specific event or match."},"auto_disable":{"behaviour":"A webhook is switched off (isActive false, reason in lastError) after 100 failures in a row, 20 in a row when the answer is permanent (401, 403, 404, 410, 530, DNS or certificate errors), or 50 in a row with no success for 7 days.","re_enable":"POST /api/v1/webhooks/{id}/reset, or PATCH with {\"isActive\":true}."},"log_note":"attempt in the delivery log is currently always 1, even when retries happened. X-Cito-Delivery-Attempt on the request is the real attempt number.","outbound_addresses":"Deliveries come from Cito servers and may change. Verify X-Cito-Signature instead of allow-listing addresses."},"filters":{"note":"Set on a webhook as {\"filters\":{\"league\":\"LEC\"}}. Each field takes one value or a list, compared case-insensitively. Fields per event are on each event.","fields":{"league":"data.league (for example LEC, Worlds, or cs2 for CS2 events).","matchId":"data.matchId.","gameId":"data.gameId.","side":"data.side.","tour":"Tennis: ATP, WTA and so on.","tournamentId":"Tennis tournament id.","playerId":"Tennis: matches either player."},"tennis_matchId":"For tennis events, matchId matches data.id."},"endpoints":{"catalog":"GET /api/v1/webhooks/events?game=&event=&examples=false&fields=false (public)","openapi":"GET /api/v1/webhooks/openapi.json (public)","list":"GET /api/v1/webhooks","get":"GET /api/v1/webhooks/{id}","create":"POST /api/v1/webhooks","update":"PATCH /api/v1/webhooks/{id}","delete":"DELETE /api/v1/webhooks/{id}","test":"POST /api/v1/webhooks/{id}/test","regenerate_secret":"POST /api/v1/webhooks/{id}/regenerate-secret","reset":"POST /api/v1/webhooks/{id}/reset","deliveries":"GET /api/v1/webhooks/{id}/deliveries","delivery":"GET /api/v1/webhooks/{id}/deliveries/{deliveryId}","replay":"POST /api/v1/webhooks/{id}/replay","catch_up":"POST /api/v1/webhooks/{id}/catch-up","inbox":"POST, GET /api/v1/webhook-inbox"},"auth":"Management routes take your API key (x-api-key header or Authorization: Bearer) or a dashboard session. A key only reaches its own account's webhooks.","measured_at":"2026-11-08T20:30:57.835Z","docs":"https://citoapi.com/docs/webhooks/","dashboard":"https://citoapi.com/dashboard/webhooks"}