{"success":true,"events":[{"event":"dota2.draft.updated","game":"dota2","kind":"lifecycle","description":"The picks and bans of a Dota 2 game, with hero names and team ids.","whenFires":"When a draft is seen for a game. Can repeat for one game as it fills in.","status":"active","statusNote":"Sent during the last 7 days.","typicalFrequency":"about 21 per day across all matches (about 3 per match)","last7Days":150,"lastSentAt":"2026-11-08T20:04:13.386Z","latency":{"p50Ms":1150,"p95Ms":2404,"deliveries":635,"basis":"From the moment Cito recorded the event to the moment a customer endpoint answered, recent deliveries."},"minimumPlan":"pro","plans":["PRO","BUSINESS","ENTERPRISE","ONE_GAME_STARTER (own game)","BASIC"],"filters":["matchId","gameId"],"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":{"average":2192,"max":2300,"sampledRows":300},"fields":[{"name":"bans","types":["array"],"presentPct":100},{"name":"bans[].heroId","types":["number"],"presentPct":100},{"name":"bans[].heroName","types":["string"],"presentPct":100},{"name":"bans[].order","types":["number"],"presentPct":100},{"name":"bans[].phase","types":["number"],"presentPct":100},{"name":"bans[].teamId","types":["string"],"presentPct":100},{"name":"direTeam","types":["string"],"presentPct":100},{"name":"draftRows","types":["number"],"presentPct":100},{"name":"gameId","types":["string"],"presentPct":100},{"name":"gameNumber","types":["number"],"presentPct":100},{"name":"matchId","types":["string"],"presentPct":100},{"name":"picks","types":["array"],"presentPct":100},{"name":"picks[].heroId","types":["number"],"presentPct":100},{"name":"picks[].heroName","types":["string"],"presentPct":100},{"name":"picks[].order","types":["number"],"presentPct":100},{"name":"picks[].phase","types":["number"],"presentPct":100},{"name":"picks[].teamId","types":["string"],"presentPct":100},{"name":"radiantTeam","types":["string"],"presentPct":100},{"name":"teams","types":["array"],"presentPct":100}],"example":{"event":"dota2.draft.updated","id":"a1b2c3d4-0000-4000-8000-000000000000","timestamp":"2026-11-08T10:57:12.737Z","event_type":"dota2.draft.updated","match_id":"663676","previous_state":null,"data":{"bans":[{"order":0,"phase":1,"heroId":33,"teamId":"cyberhero","heroName":"Enigma"},{"order":1,"phase":1,"heroId":100,"teamId":"cyberhero","heroName":"Tusk"},{"order":2,"phase":1,"heroId":62,"teamId":"angry","heroName":"Bounty Hunter"},{"order":3,"phase":1,"heroId":105,"teamId":"angry","heroName":"Techies"},{"order":4,"phase":1,"heroId":50,"teamId":"cyberhero","heroName":"Dazzle"},{"order":5,"phase":1,"heroId":155,"teamId":"angry","heroName":"Largo"},{"order":6,"phase":1,"heroId":112,"teamId":"angry","heroName":"Winter Wyvern"},{"order":9,"phase":2,"heroId":55,"teamId":"cyberhero","heroName":"Dark Seer"},{"order":10,"phase":2,"heroId":135,"teamId":"cyberhero","heroName":"Dawnbreaker"},{"order":11,"phase":2,"heroId":106,"teamId":"angry","heroName":"Ember Spirit"},{"order":18,"phase":3,"heroId":77,"teamId":"cyberhero","heroName":"Lycan"},{"order":19,"phase":3,"heroId":21,"teamId":"angry","heroName":"Windranger"},{"order":20,"phase":3,"heroId":78,"teamId":"cyberhero","heroName":"Brewmaster"},{"order":21,"phase":3,"heroId":18,"teamId":"angry","heroName":"Sven"}],"picks":[{"order":7,"phase":2,"heroId":49,"teamId":"cyberhero","heroName":"Dragon Knight"},{"order":8,"phase":2,"heroId":107,"teamId":"angry","heroName":"Earth Spirit"},{"order":12,"phase":2,"heroId":30,"teamId":"angry","heroName":"Witch Doctor"},{"order":13,"phase":2,"heroId":123,"teamId":"cyberhero","heroName":"Hoodwink"},{"order":14,"phase":2,"heroId":43,"teamId":"cyberhero","heroName":"Death Prophet"},{"order":15,"phase":2,"heroId":119,"teamId":"angry","heroName":"Dark Willow"},{"order":16,"phase":2,"heroId":80,"teamId":"angry","heroName":"Lone Druid"},{"order":17,"phase":3,"heroId":7,"teamId":"cyberhero","heroName":"Earthshaker"},{"order":22,"phase":3,"heroId":76,"teamId":"cyberhero","heroName":"Outworld Devourer"},{"order":23,"phase":3,"heroId":47,"teamId":"angry","heroName":"Viper"}],"teams":["Angry Birds","CyberHero"],"gameId":"valve-live-663676-g1","matchId":"663676","direTeam":"Angry Birds","draftRows":24,"gameNumber":1,"radiantTeam":"CyberHero"}},"exampleCapturedAt":"2026-11-08T10:57:12.737Z","docsUrl":"https://citoapi.com/docs/webhooks/#dota2-draft-updated"}],"count":1,"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"}