Docs/UFC API/Webhooks

UFC webhooks

Eight events for UFC cards, including Dana White's Contender Series and Road to UFC. No other promotion ever appears in them. The round events need Pro or above.

Which events do I need?

Live scoreboard

ufc.fight.startedufc.round.completedufc.fight.completed

The opening bell, each round, and the result.

Settle bets

ufc.fight.completedufc.fight.stats_ready

Result, method and round, then the official stats for stat markets.

Results only

ufc.fight.completedufc.event.completed

Each fight result, and the whole card when it is done.

Alerts

ufc.fight.startedufc.fight.completedufc.card.updated

Opening bell, result and card changes. Add a fighter filter to follow someone.

Filters

Set on the webhook, for example {"filters":{"eventSlug":"..."}}. Each takes one value or a list, compared case-insensitively. A filter on a field an event does not carry stops every delivery of that event, so each event below lists the filters it supports.

eventSlug
Only this card: slug or id from /ufc/events.
boutId
Only this fight: id from /ufc/events/{slug}/bouts.
fighterSlug
Only fights with this fighter. On card events, cards or changes that include them.
series
UFC, Contender Series or Road to UFC.

Recipes

One fight, in order

Rounds arrive in this order, but order your logic on dedupeKey and round, not on arrival time alone.
Text
ufc.fight.started
ufc.round.started      round 1
ufc.round.completed    round 1, endedBy bell
ufc.round.started      round 2
ufc.round.completed    round 2, endedBy finish
ufc.fight.completed
ufc.fight.stats_ready  later, when official stats are in

Follow one fighter

A fighterSlug filter matches any fight with that fighter, and on card-level events the cards or changes that include them.
JSON
{ "events": ["ufc.fight.started", "ufc.fight.completed"], "filters": { "fighterSlug": "kayla-harrison" } }

Grade, then re-grade on official stats

Settle the result on ufc.fight.completed. Grade stat markets only on ufc.fight.stats_ready. resultTime can differ by a second or two from the final official time.

More: recipes for every sport.

Good to know

  • Events are sent only while they are news: a bell seen more than 15 minutes late is not sent, a result more than 6 hours late is not sent, and stats are not announced more than 72 hours after the fight. Past cards and later corrections never fire.
  • A fight the live clock never reached has no ufc.fight.started or round events, but still gets ufc.fight.completed and ufc.fight.stats_ready.
  • Live round numbers keep settling after the bell, so ufc.round.completed marks them statsProvisional: true. ufc.fight.stats_ready has the official ones.
  • A decision can take a few minutes after the last bell, while the scorecards are read.
  • ufc.card.updated waits 10 minutes before sending, so a change that immediately reverts is never sent. A rebooked opponent appears as one cancelled and one added.
  • method values: KO/TKO, SUB, U-DEC, S-DEC, M-DEC, DQ, CNC, Overturned, and occasional longer forms.
  • data.dedupeKey never changes for one event. Every id works on the /ufc routes.

All 8 events

0 of 8 were sent in the 7 days to 2026-11-08. None recorded yet means you can subscribe, but do not rely on the event. For the current status of each, read GET /api/v1/webhooks/events?game=ufc.

ufc.event.startedNone recorded yet

A UFC card has started, with the public card in data.bouts.

When
At the opening bell of the first fight. Once per card.
Frequency
1 per card
Plan
Pro and Scale
Filters
eventSlug, boutId, fighterSlug, series
Use it to
Open your fight-night view with the card in data.bouts.
You can subscribe, but none has been recorded. Do not rely on it.

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).

Card started. Once per card, at the opening bell of its first fight (first round clock seen).

Fields in data (14)
FieldMeaning
dedupeKeyDeterministic identity of this event. Same value if it is ever delivered twice: use it as your idempotency key.
occurredAtWhen Cito recorded this, UTC.
eventIdThe card id from /ufc/events.
eventSlugThe card slug, accepted by every /ufc/events/{slug} route.
eventDataIdThe card dataId from /ufc/events.
eventTitleThe card title.
seriesufc, dwcs or road-to-ufc.
eventStartsAtThe card start time on /ufc/events.
venueVenue.
cityCity.
countryCountry.
startedAtEstimated time of the first bell.
firstBoutIdThe fight that opened the card.
boutsThe public card at that moment.
Full example
JSON
{
  "event": "ufc.event.started",
  "id": "3f6f0a4e-2c1d-5b7a-9e8f-1a2b3c4d5e6f",
  "timestamp": "2026-11-07T22:10:46.813Z",
  "event_type": "ufc.event.started",
  "match_id": null,
  "previous_state": null,
  "data": {
    "dedupeKey": "ufc.event.started|ufc-fight-night-november-07-2026",
    "occurredAt": "2026-11-07T22:10:45.313Z",
    "eventId": "0a5a83bd-c925-4cb4-bd05-293e6002fb67",
    "eventSlug": "ufc-fight-night-november-07-2026",
    "eventDataId": "110404b505fb562c",
    "eventTitle": "UFC Fight Night: Cortes Acosta vs Walker",
    "series": "ufc",
    "eventStartsAt": "2026-11-07T22:00:00.000Z",
    "venue": "Meta APEX",
    "city": "Las Vegas",
    "country": "United States",
    "startedAt": "2026-11-07T22:06:29.313Z",
    "firstBoutId": "fight-night-november-07-2026-jonny-parsons-jose-henrique",
    "bouts": [
      {
        "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
        "cardSection": "Main Card",
        "cardPosition": "Main Card 1",
        "weightClass": "Heavyweight",
        "fighters": [
          {
            "fighterId": "26d3ffaf-7b1e-4498-adc1-c129e4530fab",
            "fighterSlug": "johnny-walker",
            "fighterName": "Johnny Walker",
            "corner": "blue"
          },
          {
            "fighterId": "ae6aa622-1b62-4cac-ab64-9179aa9f07af",
            "fighterSlug": "waldo-cortes-acosta",
            "fighterName": "Waldo Cortes Acosta",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-tatiana-suarez-virna-jandiroba",
        "cardSection": "Main Card",
        "cardPosition": "Main Card 2",
        "weightClass": "Women's Strawweight",
        "fighters": [
          {
            "fighterId": "fde19553-e551-4841-a91b-bc2037335aed",
            "fighterSlug": "virna-jandiroba",
            "fighterName": "Virna Jandiroba",
            "corner": "blue"
          },
          {
            "fighterId": "8b642ed7-0509-4526-864a-29d5c446b0db",
            "fighterSlug": "tatiana-suarez",
            "fighterName": "Tatiana Suarez",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-davey-grant-elijah-smith",
        "cardSection": "Main Card",
        "cardPosition": "Main Card 3",
        "weightClass": "Bantamweight",
        "fighters": [
          {
            "fighterId": "e14b1be9-26b4-4878-a7b5-977964f7e0fa",
            "fighterSlug": "elijah-smith",
            "fighterName": "Elijah Smith",
            "corner": "blue"
          },
          {
            "fighterId": "229d15be-94f6-45be-acf9-0cc02eb21044",
            "fighterSlug": "davey-grant",
            "fighterName": "Davey Grant",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-billy-elekana-lucas-fernando",
        "cardSection": "Main Card",
        "cardPosition": "Main Card 4",
        "weightClass": "Light Heavyweight",
        "fighters": [
          {
            "fighterId": "f39ca6c4-d608-41e0-939e-5f581df28768",
            "fighterSlug": "lucas-fernando",
            "fighterName": "Lucas Fernando",
            "corner": "blue"
          },
          {
            "fighterId": "2a2cc58d-aa2f-4f77-9c81-43a9d6d60b99",
            "fighterSlug": "billy-elekana",
            "fighterName": "Billy Elekana",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-nina-milosevic-priscila-cachoeira",
        "cardSection": "Main Card",
        "cardPosition": "Main Card 5",
        "weightClass": "Women's Bantamweight",
        "fighters": [
          {
            "fighterId": "e4dc8d85-7f37-4d0f-8b7c-f00b913324c8",
            "fighterSlug": "nina-milosevic",
            "fighterName": "Nina Milošević",
            "corner": "blue"
          },
          {
            "fighterId": "2e2abb2f-7f09-4b63-87ba-44797bc47578",
            "fighterSlug": "priscila-cachoeira",
            "fighterName": "Priscila Cachoeira",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-marcos-degli-mitch-raposo",
        "cardSection": "Main Card",
        "cardPosition": "Main Card 6",
        "weightClass": "Flyweight",
        "fighters": [
          {
            "fighterId": "27a49aff-0106-4c6a-ba10-528ffc4f3ee6",
            "fighterSlug": "marcos-degli",
            "fighterName": "Marcos Degli",
            "corner": "blue"
          },
          {
            "fighterId": "b0447906-54cf-4daa-9b0d-87a9b929e4ed",
            "fighterSlug": "mitch-raposo",
            "fighterName": "Mitch Raposo",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-keiichiro-nakamura-0-ollie-schmid",
        "cardSection": "Prelims",
        "cardPosition": "Prelims 1",
        "weightClass": "Featherweight",
        "fighters": [
          {
            "fighterId": "c8f1e9d1-1ab8-434f-b882-7a32f437adfa",
            "fighterSlug": "ollie-schmid",
            "fighterName": "Ollie Schmid",
            "corner": "blue"
          },
          {
            "fighterId": "f003129d-4089-49ac-8a1f-af7dac11c7ea",
            "fighterSlug": "keiichiro-nakamura",
            "fighterName": "Keiichiro Nakamura",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-gabriel-lorenco-thomas-petersen",
        "cardSection": "Prelims",
        "cardPosition": "Prelims 2",
        "weightClass": "Heavyweight",
        "fighters": [
          {
            "fighterId": "824cbd73-34be-4587-86cc-82fb49efcace",
            "fighterSlug": "thomas-petersen",
            "fighterName": "Thomas Petersen",
            "corner": "blue"
          },
          {
            "fighterId": "79e1f6d2-4cfd-4d37-9277-8dd2ab83835f",
            "fighterSlug": "gabriel-lorenco",
            "fighterName": "Gabriel Lorenço",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-gabriella-fernandes-karine-silva",
        "cardSection": "Prelims",
        "cardPosition": "Prelims 3",
        "weightClass": "Women's Flyweight",
        "fighters": [
          {
            "fighterId": "7e20c783-25a3-41ff-9daf-3bf69e0fd96e",
            "fighterSlug": "gabriella-fernandes",
            "fighterName": "Gabriella Fernandes",
            "corner": "blue"
          },
          {
            "fighterId": "971c5892-bf16-4106-bd0c-4ac1189a8358",
            "fighterSlug": "karine-silva",
            "fighterName": "Karine Silva",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-jose-delano-murtazali-magomedov",
        "cardSection": "Prelims",
        "cardPosition": "Prelims 4",
        "weightClass": "Featherweight",
        "fighters": [
          {
            "fighterId": "57414bcb-54af-4efd-8029-cee43d38f303",
            "fighterSlug": "murtazali-magomedov",
            "fighterName": "Murtazali Magomedov",
            "corner": "blue"
          },
          {
            "fighterId": "84760727-5bb0-4146-8d8a-75ed7fc282d6",
            "fighterSlug": "jose-delano",
            "fighterName": "Jose Delano",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-austin-bashi-carlos-calderon",
        "cardSection": "Prelims",
        "cardPosition": "Prelims 5",
        "weightClass": "Featherweight",
        "fighters": [
          {
            "fighterId": "242a9091-9cd6-47da-93d3-6cd3d1482071",
            "fighterSlug": "carlos-calderon",
            "fighterName": "Carlos Calderon",
            "corner": "blue"
          },
          {
            "fighterId": "997d121e-6d0b-4916-a8b7-2c69b359b41a",
            "fighterSlug": "austin-bashi",
            "fighterName": "Austin Bashi",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-andre-petroski-mantas-kondratavicius",
        "cardSection": "Prelims",
        "cardPosition": "Prelims 6",
        "weightClass": "Middleweight",
        "fighters": [
          {
            "fighterId": "3c4fbc90-5087-454a-80a0-82a53f381455",
            "fighterSlug": "andre-petroski",
            "fighterName": "Andre Petroski",
            "corner": "blue"
          },
          {
            "fighterId": "29d9fb0c-e709-4ac8-8e94-a9e31b854575",
            "fighterSlug": "mantas-kondratavicius",
            "fighterName": "Mantas Kondratavičius",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-seokhyeon-ko-wellington-turman",
        "cardSection": "Prelims",
        "cardPosition": "Prelims 7",
        "weightClass": "Welterweight",
        "fighters": [
          {
            "fighterId": "33ec8329-09eb-4946-898f-1e0942efc5d8",
            "fighterSlug": "wellington-turman",
            "fighterName": "Wellington Turman",
            "corner": "blue"
          },
          {
            "fighterId": "370a18c8-977f-422e-a844-5d8ed65f6f50",
            "fighterSlug": "seokhyeon-ko",
            "fighterName": "Seokhyeon Ko",
            "corner": "red"
          }
        ]
      },
      {
        "boutId": "fight-night-november-07-2026-jonny-parsons-jose-henrique",
        "cardSection": "Prelims",
        "cardPosition": "Prelims 8",
        "weightClass": "Welterweight",
        "fighters": [
          {
            "fighterId": "4919764f-b74e-40ba-b57b-e3eeef512bdf",
            "fighterSlug": "jose-henrique",
            "fighterName": "José Souza",
            "corner": "blue"
          },
          {
            "fighterId": "8b348104-718f-41c5-9314-3f11096a09a7",
            "fighterSlug": "jonny-parsons",
            "fighterName": "Jonny Parsons",
            "corner": "red"
          }
        ]
      }
    ]
  }
}
ufc.fight.startedNone recorded yet

A fight has started: data.startedAt is the estimated bell.

When
At the opening bell, not the walkouts. Once per fight.
Frequency
1 per fight, 5 to 14 per card
Plan
Pro and Scale
Filters
eventSlug, boutId, fighterSlug, series
Use it to
Flip a fight to live at the opening bell.
You can subscribe, but none has been recorded. Do not rely on it.

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).

Fight started. Once per fight, at the opening bell, not at the walkouts. Not sent late: a fight whose bell was not seen within 15 minutes has no ufc.fight.started.

Fields in data (20)
FieldMeaning
dedupeKeyDeterministic identity of this event. Same value if it is ever delivered twice: use it as your idempotency key.
occurredAtWhen Cito recorded this, UTC.
eventIdThe card id from /ufc/events.
eventSlugThe card slug, accepted by every /ufc/events/{slug} route.
eventDataIdThe card dataId from /ufc/events.
eventTitleThe card title.
seriesufc, dwcs or road-to-ufc.
eventStartsAtThe card start time on /ufc/events.
boutIdThe fight id: same value as id on /ufc/events/{slug}/bouts, accepted by /ufc/bouts/{id}.
matchIdSame as boutId (the envelope match_id).
liveBoutIdThe id on /ufc/live, the stream and socket rooms.
dataIdThe fight dataId, also accepted by /ufc/bouts/{id}.
cardSectionMain Card, Prelims or Early Prelims.
cardPositionSlot on the card, for example "Main Card 1".
weightClassThe division of the fight.
titleBoutTrue for a title fight.
fightersBoth fighters: fighterId, fighterSlug, fighterName, corner. Same ids and corners as /ufc/bouts/{id}.
startedAtEstimated time of the bell (first clock reading minus its elapsed time).
roundAlways 1.
clockElapsed time on the clock when first seen, usually under 15 seconds.
Full example
JSON
{
  "event": "ufc.fight.started",
  "id": "3f6f0a4e-2c1d-5b7a-9e8f-1a2b3c4d5e6f",
  "timestamp": "2026-11-08T03:47:59.660Z",
  "event_type": "ufc.fight.started",
  "match_id": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
  "previous_state": null,
  "data": {
    "dedupeKey": "ufc.fight.started|fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "occurredAt": "2026-11-08T03:47:58.160Z",
    "eventId": "0a5a83bd-c925-4cb4-bd05-293e6002fb67",
    "eventSlug": "ufc-fight-night-november-07-2026",
    "eventDataId": "110404b505fb562c",
    "eventTitle": "UFC Fight Night: Cortes Acosta vs Walker",
    "series": "ufc",
    "eventStartsAt": "2026-11-07T22:00:00.000Z",
    "matchId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "liveBoutId": "ufc-fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "dataId": "13238",
    "cardSection": "Main Card",
    "cardPosition": "Main Card 1",
    "weightClass": "Heavyweight",
    "titleBout": false,
    "fighters": [
      {
        "fighterId": "26d3ffaf-7b1e-4498-adc1-c129e4530fab",
        "fighterSlug": "johnny-walker",
        "fighterName": "Johnny Walker",
        "corner": "blue"
      },
      {
        "fighterId": "ae6aa622-1b62-4cac-ab64-9179aa9f07af",
        "fighterSlug": "waldo-cortes-acosta",
        "fighterName": "Waldo Cortes Acosta",
        "corner": "red"
      }
    ],
    "startedAt": "2026-11-08T03:47:52.160Z",
    "round": 1,
    "clock": "0:06"
  }
}
ufc.round.startedNone recorded yet

A round has started: data.round.

When
When the clock of that round is first seen. Once per round.
Frequency
1 to 5 per fight
Plan
Pro and Scale (real time)
Filters
eventSlug, boutId, fighterSlug, series
Use it to
Show the current round.
You can subscribe, but none has been recorded. Do not rely on it.

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).

Round started. Once per round of each fight, when that round's clock is first seen. Round 1 arrives right after ufc.fight.started.

Fields in data (20)
FieldMeaning
dedupeKeyDeterministic identity of this event. Same value if it is ever delivered twice: use it as your idempotency key.
occurredAtWhen Cito recorded this, UTC.
eventIdThe card id from /ufc/events.
eventSlugThe card slug, accepted by every /ufc/events/{slug} route.
eventDataIdThe card dataId from /ufc/events.
eventTitleThe card title.
seriesufc, dwcs or road-to-ufc.
eventStartsAtThe card start time on /ufc/events.
boutIdThe fight id: same value as id on /ufc/events/{slug}/bouts, accepted by /ufc/bouts/{id}.
matchIdSame as boutId (the envelope match_id).
liveBoutIdThe id on /ufc/live, the stream and socket rooms.
dataIdThe fight dataId, also accepted by /ufc/bouts/{id}.
cardSectionMain Card, Prelims or Early Prelims.
cardPositionSlot on the card, for example "Main Card 1".
weightClassThe division of the fight.
titleBoutTrue for a title fight.
fightersBoth fighters: fighterId, fighterSlug, fighterName, corner. Same ids and corners as /ufc/bouts/{id}.
roundRound number, from 1.
clockElapsed time on the clock when first seen.
startedAtEstimated start of the round.
Full example
JSON
{
  "event": "ufc.round.started",
  "id": "3f6f0a4e-2c1d-5b7a-9e8f-1a2b3c4d5e6f",
  "timestamp": "2026-11-08T03:54:15.798Z",
  "event_type": "ufc.round.started",
  "match_id": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
  "previous_state": null,
  "data": {
    "dedupeKey": "ufc.round.started|fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta|2",
    "occurredAt": "2026-11-08T03:54:14.298Z",
    "eventId": "0a5a83bd-c925-4cb4-bd05-293e6002fb67",
    "eventSlug": "ufc-fight-night-november-07-2026",
    "eventDataId": "110404b505fb562c",
    "eventTitle": "UFC Fight Night: Cortes Acosta vs Walker",
    "series": "ufc",
    "eventStartsAt": "2026-11-07T22:00:00.000Z",
    "matchId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "liveBoutId": "ufc-fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "dataId": "13238",
    "cardSection": "Main Card",
    "cardPosition": "Main Card 1",
    "weightClass": "Heavyweight",
    "titleBout": false,
    "fighters": [
      {
        "fighterId": "26d3ffaf-7b1e-4498-adc1-c129e4530fab",
        "fighterSlug": "johnny-walker",
        "fighterName": "Johnny Walker",
        "corner": "blue"
      },
      {
        "fighterId": "ae6aa622-1b62-4cac-ab64-9179aa9f07af",
        "fighterSlug": "waldo-cortes-acosta",
        "fighterName": "Waldo Cortes Acosta",
        "corner": "red"
      }
    ],
    "round": 2,
    "clock": "0:10",
    "startedAt": "2026-11-08T03:54:04.298Z"
  }
}
ufc.round.completedNone recorded yet

A round has ended: data.round, data.endedBy (bell or finish), and each fighter's provisional round numbers in data.stats.

When
At the bell, or when the fight ends in that round. Once per round.
Frequency
1 to 5 per fight
Plan
Pro and Scale (real time)
Filters
eventSlug, boutId, fighterSlug, series
Use it to
Show round numbers. They are marked provisional: the official ones arrive with ufc.fight.stats_ready.
You can subscribe, but none has been recorded. Do not rely on it.

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).

Round completed. Once per round: when the clock reaches 5:00, the next round is seen, or the fight ends in that round.

Fields in data (22)
FieldMeaning
dedupeKeyDeterministic identity of this event. Same value if it is ever delivered twice: use it as your idempotency key.
occurredAtWhen Cito recorded this, UTC.
eventIdThe card id from /ufc/events.
eventSlugThe card slug, accepted by every /ufc/events/{slug} route.
eventDataIdThe card dataId from /ufc/events.
eventTitleThe card title.
seriesufc, dwcs or road-to-ufc.
eventStartsAtThe card start time on /ufc/events.
boutIdThe fight id: same value as id on /ufc/events/{slug}/bouts, accepted by /ufc/bouts/{id}.
matchIdSame as boutId (the envelope match_id).
liveBoutIdThe id on /ufc/live, the stream and socket rooms.
dataIdThe fight dataId, also accepted by /ufc/bouts/{id}.
cardSectionMain Card, Prelims or Early Prelims.
cardPositionSlot on the card, for example "Main Card 1".
weightClassThe division of the fight.
titleBoutTrue for a title fight.
fightersBoth fighters: fighterId, fighterSlug, fighterName, corner. Same ids and corners as /ufc/bouts/{id}.
roundRound number.
endedBybell, or finish when the fight ended inside the round.
clock5:00 at the bell, otherwise the finish time.
statsProvisionalAlways true: round numbers can still settle after the bell. ufc.fight.stats_ready has the official ones.
statsEach fighter's numbers for this round, same fields as /ufc/bouts/{id}/rounds.
Full example
JSON
{
  "event": "ufc.round.completed",
  "id": "3f6f0a4e-2c1d-5b7a-9e8f-1a2b3c4d5e6f",
  "timestamp": "2026-11-08T04:05:34.400Z",
  "event_type": "ufc.round.completed",
  "match_id": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
  "previous_state": null,
  "data": {
    "dedupeKey": "ufc.round.completed|fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta|3",
    "occurredAt": "2026-11-08T04:05:32.900Z",
    "eventId": "0a5a83bd-c925-4cb4-bd05-293e6002fb67",
    "eventSlug": "ufc-fight-night-november-07-2026",
    "eventDataId": "110404b505fb562c",
    "eventTitle": "UFC Fight Night: Cortes Acosta vs Walker",
    "series": "ufc",
    "eventStartsAt": "2026-11-07T22:00:00.000Z",
    "matchId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "liveBoutId": "ufc-fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "dataId": "13238",
    "cardSection": "Main Card",
    "cardPosition": "Main Card 1",
    "weightClass": "Heavyweight",
    "titleBout": false,
    "fighters": [
      {
        "fighterId": "26d3ffaf-7b1e-4498-adc1-c129e4530fab",
        "fighterSlug": "johnny-walker",
        "fighterName": "Johnny Walker",
        "corner": "blue"
      },
      {
        "fighterId": "ae6aa622-1b62-4cac-ab64-9179aa9f07af",
        "fighterSlug": "waldo-cortes-acosta",
        "fighterName": "Waldo Cortes Acosta",
        "corner": "red"
      }
    ],
    "round": 3,
    "endedBy": "finish",
    "clock": "2:27",
    "statsProvisional": true,
    "stats": [
      {
        "fighterSlug": "waldo-cortes-acosta",
        "fighterName": "Waldo Cortes Acosta",
        "round": 3,
        "knockdowns": 0,
        "significantStrikes": "5 of 18",
        "totalStrikes": "7 of 20",
        "takedowns": "0 of 0",
        "submissionAttempts": 0,
        "reversals": 0,
        "controlTime": "1:07",
        "head": "5 of 18",
        "body": "0 of 0",
        "leg": "0 of 0",
        "distance": "4 of 15",
        "clinch": "1 of 3",
        "ground": "0 of 0"
      },
      {
        "fighterSlug": "johnny-walker",
        "fighterName": "Johnny Walker",
        "round": 3,
        "knockdowns": 1,
        "significantStrikes": "12 of 23",
        "totalStrikes": "12 of 23",
        "takedowns": "0 of 0",
        "submissionAttempts": 0,
        "reversals": 0,
        "controlTime": "0:09",
        "head": "8 of 18",
        "body": "1 of 2",
        "leg": "3 of 3",
        "distance": "11 of 20",
        "clinch": "0 of 2",
        "ground": "1 of 1"
      }
    ]
  }
}
ufc.fight.completedNone recorded yet

A fight result: data.outcome (win, draw or no_contest), data.winner, data.loser, data.method, data.methodDetails, data.resultRound, data.resultTime.

When
When the result is held. Once per fight.
Frequency
1 per fight
Plan
Pro and Scale
Filters
eventSlug, boutId, fighterSlug, series
Use it to
Settle on data.outcome, data.winner, data.method and data.resultRound.
You can subscribe, but none has been recorded. Do not rely on it.

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).

Fight result. Once per fight, when the result is held. A result can take a few minutes after the finish on decisions, while the scorecards are read.

Fields in data (26)
FieldMeaning
dedupeKeyDeterministic identity of this event. Same value if it is ever delivered twice: use it as your idempotency key.
occurredAtWhen Cito recorded this, UTC.
eventIdThe card id from /ufc/events.
eventSlugThe card slug, accepted by every /ufc/events/{slug} route.
eventDataIdThe card dataId from /ufc/events.
eventTitleThe card title.
seriesufc, dwcs or road-to-ufc.
eventStartsAtThe card start time on /ufc/events.
boutIdThe fight id: same value as id on /ufc/events/{slug}/bouts, accepted by /ufc/bouts/{id}.
matchIdSame as boutId (the envelope match_id).
liveBoutIdThe id on /ufc/live, the stream and socket rooms.
dataIdThe fight dataId, also accepted by /ufc/bouts/{id}.
cardSectionMain Card, Prelims or Early Prelims.
cardPositionSlot on the card, for example "Main Card 1".
weightClassThe division of the fight.
titleBoutTrue for a title fight.
fightersBoth fighters: fighterId, fighterSlug, fighterName, corner. Same ids and corners as /ufc/bouts/{id}.
completedAtWhen the result was held.
outcomewin, draw or no_contest.
winnerfighterId, fighterSlug, fighterName, corner. null on a draw or no contest.
loserSame shape as winner.
winnerFighterSlugSame value as /ufc/bouts/{id}.
methodKO/TKO, SUB, U-DEC, S-DEC, M-DEC, DQ, CNC, Overturned and similar.
methodDetailsFinish detail such as "Punch". May be null.
resultRoundRound the fight ended in.
resultTimeTime in that round. Can differ by a second or two from the final official time; ufc.fight.stats_ready and /ufc/bouts/{id} hold the final value.
Full example
JSON
{
  "event": "ufc.fight.completed",
  "id": "3f6f0a4e-2c1d-5b7a-9e8f-1a2b3c4d5e6f",
  "timestamp": "2026-11-08T04:05:34.400Z",
  "event_type": "ufc.fight.completed",
  "match_id": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
  "previous_state": null,
  "data": {
    "dedupeKey": "ufc.fight.completed|fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "occurredAt": "2026-11-08T04:05:32.900Z",
    "eventId": "0a5a83bd-c925-4cb4-bd05-293e6002fb67",
    "eventSlug": "ufc-fight-night-november-07-2026",
    "eventDataId": "110404b505fb562c",
    "eventTitle": "UFC Fight Night: Cortes Acosta vs Walker",
    "series": "ufc",
    "eventStartsAt": "2026-11-07T22:00:00.000Z",
    "matchId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "liveBoutId": "ufc-fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "dataId": "13238",
    "cardSection": "Main Card",
    "cardPosition": "Main Card 1",
    "weightClass": "Heavyweight",
    "titleBout": false,
    "fighters": [
      {
        "fighterId": "26d3ffaf-7b1e-4498-adc1-c129e4530fab",
        "fighterSlug": "johnny-walker",
        "fighterName": "Johnny Walker",
        "corner": "blue"
      },
      {
        "fighterId": "ae6aa622-1b62-4cac-ab64-9179aa9f07af",
        "fighterSlug": "waldo-cortes-acosta",
        "fighterName": "Waldo Cortes Acosta",
        "corner": "red"
      }
    ],
    "completedAt": "2026-11-08T04:05:32.900Z",
    "outcome": "win",
    "winner": {
      "fighterId": "26d3ffaf-7b1e-4498-adc1-c129e4530fab",
      "fighterSlug": "johnny-walker",
      "fighterName": "Johnny Walker",
      "corner": "blue"
    },
    "loser": {
      "fighterId": "ae6aa622-1b62-4cac-ab64-9179aa9f07af",
      "fighterSlug": "waldo-cortes-acosta",
      "fighterName": "Waldo Cortes Acosta",
      "corner": "red"
    },
    "winnerFighterSlug": "johnny-walker",
    "method": "KO/TKO",
    "methodDetails": "Punch",
    "resultRound": 3,
    "resultTime": "2:27"
  }
}
ufc.fight.stats_readyNone recorded yet

The official whole-fight and per-round stats of a finished fight are available, in data.boutStats and data.roundStats, with the final result.

When
When both fighters have stats for every round fought. Once per fight.
Frequency
1 per fight, minutes to a few hours after the result
Plan
Pro and Scale
Filters
eventSlug, boutId, fighterSlug, series
Use it to
Store the official whole-fight and per-round stats. Re-grade anything you graded on provisional numbers.
You can subscribe, but none has been recorded. Do not rely on it.

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).

Official stats ready. Once per fight, when whole-fight and per-round official stats are held for both fighters for every round fought.

Fields in data (28)
FieldMeaning
dedupeKeyDeterministic identity of this event. Same value if it is ever delivered twice: use it as your idempotency key.
occurredAtWhen Cito recorded this, UTC.
eventIdThe card id from /ufc/events.
eventSlugThe card slug, accepted by every /ufc/events/{slug} route.
eventDataIdThe card dataId from /ufc/events.
eventTitleThe card title.
seriesufc, dwcs or road-to-ufc.
eventStartsAtThe card start time on /ufc/events.
boutIdThe fight id: same value as id on /ufc/events/{slug}/bouts, accepted by /ufc/bouts/{id}.
matchIdSame as boutId (the envelope match_id).
liveBoutIdThe id on /ufc/live, the stream and socket rooms.
dataIdThe fight dataId, also accepted by /ufc/bouts/{id}.
cardSectionMain Card, Prelims or Early Prelims.
cardPositionSlot on the card, for example "Main Card 1".
weightClassThe division of the fight.
titleBoutTrue for a title fight.
fightersBoth fighters: fighterId, fighterSlug, fighterName, corner. Same ids and corners as /ufc/bouts/{id}.
outcomeAs on ufc.fight.completed.
winnerAs on ufc.fight.completed.
loserAs on ufc.fight.completed.
winnerFighterSlugAs on ufc.fight.completed.
methodFinal method.
methodDetailsFinal detail.
resultRoundFinal round.
resultTimeFinal time.
scorecardsJudges' cards on a decision, otherwise null.
boutStatsOne row per fighter, same fields as /ufc/bouts/{id}/stats boutStats.
roundStatsOne row per fighter per round, same fields as /ufc/bouts/{id}/stats roundStats.
Full example
JSON
{
  "event": "ufc.fight.stats_ready",
  "id": "3f6f0a4e-2c1d-5b7a-9e8f-1a2b3c4d5e6f",
  "timestamp": "2026-11-08T09:11:34.003Z",
  "event_type": "ufc.fight.stats_ready",
  "match_id": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
  "previous_state": null,
  "data": {
    "dedupeKey": "ufc.fight.stats_ready|fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "occurredAt": "2026-11-08T09:11:32.503Z",
    "eventId": "0a5a83bd-c925-4cb4-bd05-293e6002fb67",
    "eventSlug": "ufc-fight-night-november-07-2026",
    "eventDataId": "110404b505fb562c",
    "eventTitle": "UFC Fight Night: Cortes Acosta vs Walker",
    "series": "ufc",
    "eventStartsAt": "2026-11-07T22:00:00.000Z",
    "matchId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "liveBoutId": "ufc-fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
    "dataId": "13238",
    "cardSection": "Main Card",
    "cardPosition": "Main Card 1",
    "weightClass": "Heavyweight",
    "titleBout": false,
    "fighters": [
      {
        "fighterId": "26d3ffaf-7b1e-4498-adc1-c129e4530fab",
        "fighterSlug": "johnny-walker",
        "fighterName": "Johnny Walker",
        "corner": "blue"
      },
      {
        "fighterId": "ae6aa622-1b62-4cac-ab64-9179aa9f07af",
        "fighterSlug": "waldo-cortes-acosta",
        "fighterName": "Waldo Cortes Acosta",
        "corner": "red"
      }
    ],
    "outcome": "win",
    "winner": {
      "fighterId": "26d3ffaf-7b1e-4498-adc1-c129e4530fab",
      "fighterSlug": "johnny-walker",
      "fighterName": "Johnny Walker",
      "corner": "blue"
    },
    "loser": {
      "fighterId": "ae6aa622-1b62-4cac-ab64-9179aa9f07af",
      "fighterSlug": "waldo-cortes-acosta",
      "fighterName": "Waldo Cortes Acosta",
      "corner": "red"
    },
    "winnerFighterSlug": "johnny-walker",
    "method": "KO/TKO",
    "methodDetails": "Punch",
    "resultRound": 3,
    "resultTime": "2:27",
    "scorecards": null,
    "boutStats": [
      {
        "fighterSlug": "johnny-walker",
        "fighterName": "Johnny Walker",
        "knockdowns": 1,
        "significantStrikes": "51 of 77",
        "totalStrikes": "61 of 87",
        "takedowns": "0 of 0",
        "submissionAttempts": 0,
        "reversals": 0,
        "controlTime": "0:09",
        "head": "17 of 42",
        "body": "11 of 12",
        "leg": "23 of 23",
        "distance": "45 of 68",
        "clinch": "5 of 8",
        "ground": "1 of 1",
        "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta"
      },
      {
        "fighterSlug": "waldo-cortes-acosta",
        "fighterName": "Waldo Cortes Acosta",
        "knockdowns": 1,
        "significantStrikes": "40 of 89",
        "totalStrikes": "67 of 117",
        "takedowns": "0 of 0",
        "submissionAttempts": 0,
        "reversals": 0,
        "controlTime": "4:24",
        "head": "36 of 85",
        "body": "3 of 3",
        "leg": "1 of 1",
        "distance": "28 of 71",
        "clinch": "10 of 15",
        "ground": "2 of 3",
        "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta"
      }
    ],
    "roundStats": [
      {
        "round": 1,
        "fighterSlug": "johnny-walker",
        "fighterName": "Johnny Walker",
        "knockdowns": 0,
        "significantStrikes": "25 of 36",
        "totalStrikes": "25 of 36",
        "takedowns": "0 of 0",
        "submissionAttempts": 0,
        "reversals": 0,
        "controlTime": "0:00",
        "head": "7 of 18",
        "body": "6 of 6",
        "leg": "12 of 12",
        "distance": "23 of 34",
        "clinch": "2 of 2",
        "ground": "0 of 0",
        "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta"
      },
      {
        "round": 1,
        "fighterSlug": "waldo-cortes-acosta",
        "fighterName": "Waldo Cortes Acosta",
        "knockdowns": 1,
        "significantStrikes": "20 of 47",
        "totalStrikes": "23 of 51",
        "takedowns": "0 of 0",
        "submissionAttempts": 0,
        "reversals": 0,
        "controlTime": "1:23",
        "head": "18 of 45",
        "body": "2 of 2",
        "leg": "0 of 0",
        "distance": "13 of 36",
        "clinch": "7 of 10",
        "ground": "0 of 1",
        "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta"
      },
      {
        "round": 2,
        "fighterSlug": "johnny-walker",
        "fighterName": "Johnny Walker",
        "knockdowns": 0,
        "significantStrikes": "11 of 21",
        "totalStrikes": "21 of 31",
        "takedowns": "0 of 0",
        "submissionAttempts": 0,
        "reversals": 0,
        "controlTime": "0:00",
        "head": "1 of 10",
        "body": "2 of 3",
        "leg": "8 of 8",
        "distance": "11 of 19",
        "clinch": "0 of 2",
        "ground": "0 of 0",
        "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta"
      },
      {
        "round": 2,
        "fighterSlug": "waldo-cortes-acosta",
        "fighterName": "Waldo Cortes Acosta",
        "knockdowns": 0,
        "significantStrikes": "15 of 27",
        "totalStrikes": "36 of 48",
        "takedowns": "0 of 0",
        "submissionAttempts": 0,
        "reversals": 0,
        "controlTime": "1:54",
        "head": "13 of 25",
        "body": "1 of 1",
        "leg": "1 of 1",
        "distance": "11 of 21",
        "clinch": "2 of 4",
        "ground": "2 of 2",
        "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta"
      },
      {
        "round": 3,
        "fighterSlug": "johnny-walker",
        "fighterName": "Johnny Walker",
        "knockdowns": 1,
        "significantStrikes": "15 of 20",
        "totalStrikes": "15 of 20",
        "takedowns": "0 of 0",
        "submissionAttempts": 0,
        "reversals": 0,
        "controlTime": "0:09",
        "head": "9 of 14",
        "body": "3 of 3",
        "leg": "3 of 3",
        "distance": "11 of 15",
        "clinch": "3 of 4",
        "ground": "1 of 1",
        "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta"
      },
      {
        "round": 3,
        "fighterSlug": "waldo-cortes-acosta",
        "fighterName": "Waldo Cortes Acosta",
        "knockdowns": 0,
        "significantStrikes": "5 of 15",
        "totalStrikes": "8 of 18",
        "takedowns": "0 of 0",
        "submissionAttempts": 0,
        "reversals": 0,
        "controlTime": "1:07",
        "head": "5 of 15",
        "body": "0 of 0",
        "leg": "0 of 0",
        "distance": "4 of 14",
        "clinch": "1 of 1",
        "ground": "0 of 0",
        "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta"
      }
    ]
  }
}
ufc.card.updatedNone recorded yet

A UFC card changed: data.changes lists bouts added, moved between sections or cancelled.

When
When a change on an upcoming or running card has held for 10 minutes.
Frequency
Up to a few per card in fight week, none on most days
Plan
Pro and Scale
Filters
eventSlug, boutId, fighterSlug, series
Use it to
Refresh a card page: data.changes lists bouts added, moved or cancelled.
You can subscribe, but none has been recorded. Do not rely on it.

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).

Card changed. When a two-fighter bout is added to, moved between sections of, or cancelled from an upcoming or running card. Held for 10 minutes first, so a change that immediately reverts is not sent. Changes of order inside a section are not sent.

Fields in data (10)
FieldMeaning
dedupeKeyDeterministic identity of this event. Same value if it is ever delivered twice: use it as your idempotency key.
occurredAtWhen Cito recorded this, UTC.
eventIdThe card id from /ufc/events.
eventSlugThe card slug, accepted by every /ufc/events/{slug} route.
eventDataIdThe card dataId from /ufc/events.
eventTitleThe card title.
seriesufc, dwcs or road-to-ufc.
eventStartsAtThe card start time on /ufc/events.
revisionIncreases by 1 with each update of this card.
changesList of {type: added | moved | cancelled, boutId, weightClass, fighters, cardSection, cardPosition, from (moved only)}.
Full example
JSON
{
  "event": "ufc.card.updated",
  "id": "3f6f0a4e-2c1d-5b7a-9e8f-1a2b3c4d5e6f",
  "timestamp": "2026-11-08T18:11:02.500Z",
  "event_type": "ufc.card.updated",
  "match_id": null,
  "previous_state": null,
  "data": {
    "dedupeKey": "ufc.card.updated|ufc-334|1",
    "occurredAt": "2026-11-08T18:11:01.000Z",
    "eventId": "a380d664-081b-48b7-a069-5815ad86ff08",
    "eventSlug": "ufc-334",
    "eventDataId": "a380d664-081b-48b7-a069-5815ad86ff08",
    "eventTitle": "UFC 334",
    "series": "ufc",
    "eventStartsAt": "2026-11-14T22:00:00.000Z",
    "revision": 1,
    "changes": [
      {
        "type": "added",
        "boutId": "334-cesar-almeida-donte-johnson",
        "weightClass": "Middleweight",
        "fighters": [
          {
            "fighterId": "d4e20d36-1ff0-40d8-88c9-7715baff7b9d",
            "fighterSlug": "cesar-almeida",
            "fighterName": "Cesar Almeida",
            "corner": "blue"
          },
          {
            "fighterId": "7e06193c-f1fc-4efa-8037-f59c279d7dd8",
            "fighterSlug": "donte-johnson",
            "fighterName": "Donte Johnson",
            "corner": "red"
          }
        ],
        "cardSection": "Prelims",
        "cardPosition": "Prelims 4"
      },
      {
        "type": "cancelled",
        "boutId": "334-baisangur-susurkaev-donte-johnson",
        "weightClass": "Middleweight",
        "fighters": [
          {
            "fighterId": "f038c50e-983d-4b03-b4b6-8c67cd7324fe",
            "fighterSlug": "baisangur-susurkaev",
            "fighterName": "Baisangur Susurkaev",
            "corner": "blue"
          },
          {
            "fighterId": "7e06193c-f1fc-4efa-8037-f59c279d7dd8",
            "fighterSlug": "donte-johnson",
            "fighterName": "Donte Johnson",
            "corner": "red"
          }
        ],
        "cardSection": "Prelims",
        "cardPosition": "Prelims 4"
      }
    ]
  }
}
ufc.event.completedNone recorded yet

A UFC card has finished, with every result in data.results.

When
When every public fight has a result. Once per card.
Frequency
1 per card
Plan
Pro and Scale
Filters
eventSlug, boutId, fighterSlug, series
Use it to
Close the card with every result in data.results.
You can subscribe, but none has been recorded. Do not rely on it.

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).

Card completed. Once per card, when every public fight has a result.

Fields in data (12)
FieldMeaning
dedupeKeyDeterministic identity of this event. Same value if it is ever delivered twice: use it as your idempotency key.
occurredAtWhen Cito recorded this, UTC.
eventIdThe card id from /ufc/events.
eventSlugThe card slug, accepted by every /ufc/events/{slug} route.
eventDataIdThe card dataId from /ufc/events.
eventTitleThe card title.
seriesufc, dwcs or road-to-ufc.
eventStartsAtThe card start time on /ufc/events.
completedAtWhen the last result was held.
boutsCompletedNumber of fights with a result.
boutsWithoutResultPublic fights with no result yet, normally empty.
resultsEach fight: boutId, cardPosition, fighters, outcome, winnerFighterSlug, method, methodDetails, resultRound, resultTime.
Full example
JSON
{
  "event": "ufc.event.completed",
  "id": "3f6f0a4e-2c1d-5b7a-9e8f-1a2b3c4d5e6f",
  "timestamp": "2026-11-08T04:07:51.905Z",
  "event_type": "ufc.event.completed",
  "match_id": null,
  "previous_state": null,
  "data": {
    "dedupeKey": "ufc.event.completed|ufc-fight-night-november-07-2026",
    "occurredAt": "2026-11-08T04:07:50.405Z",
    "eventId": "0a5a83bd-c925-4cb4-bd05-293e6002fb67",
    "eventSlug": "ufc-fight-night-november-07-2026",
    "eventDataId": "110404b505fb562c",
    "eventTitle": "UFC Fight Night: Cortes Acosta vs Walker",
    "series": "ufc",
    "eventStartsAt": "2026-11-07T22:00:00.000Z",
    "completedAt": "2026-11-08T04:07:50.405Z",
    "boutsCompleted": 14,
    "boutsWithoutResult": [],
    "results": [
      {
        "boutId": "fight-night-november-07-2026-johnny-walker-waldo-cortes-acosta",
        "cardPosition": "Main Card 1",
        "fighters": [
          {
            "fighterId": "26d3ffaf-7b1e-4498-adc1-c129e4530fab",
            "fighterSlug": "johnny-walker",
            "fighterName": "Johnny Walker",
            "corner": "blue"
          },
          {
            "fighterId": "ae6aa622-1b62-4cac-ab64-9179aa9f07af",
            "fighterSlug": "waldo-cortes-acosta",
            "fighterName": "Waldo Cortes Acosta",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "johnny-walker",
        "method": "KO/TKO",
        "methodDetails": "Punch",
        "resultRound": 3,
        "resultTime": "2:27"
      },
      {
        "boutId": "fight-night-november-07-2026-tatiana-suarez-virna-jandiroba",
        "cardPosition": "Main Card 2",
        "fighters": [
          {
            "fighterId": "fde19553-e551-4841-a91b-bc2037335aed",
            "fighterSlug": "virna-jandiroba",
            "fighterName": "Virna Jandiroba",
            "corner": "blue"
          },
          {
            "fighterId": "8b642ed7-0509-4526-864a-29d5c446b0db",
            "fighterSlug": "tatiana-suarez",
            "fighterName": "Tatiana Suarez",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "tatiana-suarez",
        "method": "U-DEC",
        "methodDetails": null,
        "resultRound": 3,
        "resultTime": "5:00"
      },
      {
        "boutId": "fight-night-november-07-2026-davey-grant-elijah-smith",
        "cardPosition": "Main Card 3",
        "fighters": [
          {
            "fighterId": "e14b1be9-26b4-4878-a7b5-977964f7e0fa",
            "fighterSlug": "elijah-smith",
            "fighterName": "Elijah Smith",
            "corner": "blue"
          },
          {
            "fighterId": "229d15be-94f6-45be-acf9-0cc02eb21044",
            "fighterSlug": "davey-grant",
            "fighterName": "Davey Grant",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "elijah-smith",
        "method": "KO/TKO",
        "methodDetails": "Punches",
        "resultRound": 1,
        "resultTime": "1:00"
      },
      {
        "boutId": "fight-night-november-07-2026-billy-elekana-lucas-fernando",
        "cardPosition": "Main Card 4",
        "fighters": [
          {
            "fighterId": "f39ca6c4-d608-41e0-939e-5f581df28768",
            "fighterSlug": "lucas-fernando",
            "fighterName": "Lucas Fernando",
            "corner": "blue"
          },
          {
            "fighterId": "2a2cc58d-aa2f-4f77-9c81-43a9d6d60b99",
            "fighterSlug": "billy-elekana",
            "fighterName": "Billy Elekana",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "lucas-fernando",
        "method": "U-DEC",
        "methodDetails": null,
        "resultRound": 3,
        "resultTime": "5:00"
      },
      {
        "boutId": "fight-night-november-07-2026-nina-milosevic-priscila-cachoeira",
        "cardPosition": "Main Card 5",
        "fighters": [
          {
            "fighterId": "e4dc8d85-7f37-4d0f-8b7c-f00b913324c8",
            "fighterSlug": "nina-milosevic",
            "fighterName": "Nina Milošević",
            "corner": "blue"
          },
          {
            "fighterId": "2e2abb2f-7f09-4b63-87ba-44797bc47578",
            "fighterSlug": "priscila-cachoeira",
            "fighterName": "Priscila Cachoeira",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "nina-milosevic",
        "method": "KO/TKO",
        "methodDetails": "Elbows",
        "resultRound": 2,
        "resultTime": "3:40"
      },
      {
        "boutId": "fight-night-november-07-2026-marcos-degli-mitch-raposo",
        "cardPosition": "Main Card 6",
        "fighters": [
          {
            "fighterId": "27a49aff-0106-4c6a-ba10-528ffc4f3ee6",
            "fighterSlug": "marcos-degli",
            "fighterName": "Marcos Degli",
            "corner": "blue"
          },
          {
            "fighterId": "b0447906-54cf-4daa-9b0d-87a9b929e4ed",
            "fighterSlug": "mitch-raposo",
            "fighterName": "Mitch Raposo",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "mitch-raposo",
        "method": "U-DEC",
        "methodDetails": null,
        "resultRound": 3,
        "resultTime": "5:00"
      },
      {
        "boutId": "fight-night-november-07-2026-keiichiro-nakamura-0-ollie-schmid",
        "cardPosition": "Prelims 1",
        "fighters": [
          {
            "fighterId": "c8f1e9d1-1ab8-434f-b882-7a32f437adfa",
            "fighterSlug": "ollie-schmid",
            "fighterName": "Ollie Schmid",
            "corner": "blue"
          },
          {
            "fighterId": "f003129d-4089-49ac-8a1f-af7dac11c7ea",
            "fighterSlug": "keiichiro-nakamura",
            "fighterName": "Keiichiro Nakamura",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "ollie-schmid",
        "method": "U-DEC",
        "methodDetails": null,
        "resultRound": 3,
        "resultTime": "5:00"
      },
      {
        "boutId": "fight-night-november-07-2026-gabriel-lorenco-thomas-petersen",
        "cardPosition": "Prelims 2",
        "fighters": [
          {
            "fighterId": "824cbd73-34be-4587-86cc-82fb49efcace",
            "fighterSlug": "thomas-petersen",
            "fighterName": "Thomas Petersen",
            "corner": "blue"
          },
          {
            "fighterId": "79e1f6d2-4cfd-4d37-9277-8dd2ab83835f",
            "fighterSlug": "gabriel-lorenco",
            "fighterName": "Gabriel Lorenço",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "thomas-petersen",
        "method": "KO/TKO",
        "methodDetails": "Punches",
        "resultRound": 3,
        "resultTime": "4:47"
      },
      {
        "boutId": "fight-night-november-07-2026-gabriella-fernandes-karine-silva",
        "cardPosition": "Prelims 3",
        "fighters": [
          {
            "fighterId": "7e20c783-25a3-41ff-9daf-3bf69e0fd96e",
            "fighterSlug": "gabriella-fernandes",
            "fighterName": "Gabriella Fernandes",
            "corner": "blue"
          },
          {
            "fighterId": "971c5892-bf16-4106-bd0c-4ac1189a8358",
            "fighterSlug": "karine-silva",
            "fighterName": "Karine Silva",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "karine-silva",
        "method": "U-DEC",
        "methodDetails": null,
        "resultRound": 3,
        "resultTime": "5:00"
      },
      {
        "boutId": "fight-night-november-07-2026-jose-delano-murtazali-magomedov",
        "cardPosition": "Prelims 4",
        "fighters": [
          {
            "fighterId": "57414bcb-54af-4efd-8029-cee43d38f303",
            "fighterSlug": "murtazali-magomedov",
            "fighterName": "Murtazali Magomedov",
            "corner": "blue"
          },
          {
            "fighterId": "84760727-5bb0-4146-8d8a-75ed7fc282d6",
            "fighterSlug": "jose-delano",
            "fighterName": "Jose Delano",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "murtazali-magomedov",
        "method": "U-DEC",
        "methodDetails": null,
        "resultRound": 3,
        "resultTime": "5:00"
      },
      {
        "boutId": "fight-night-november-07-2026-austin-bashi-carlos-calderon",
        "cardPosition": "Prelims 5",
        "fighters": [
          {
            "fighterId": "242a9091-9cd6-47da-93d3-6cd3d1482071",
            "fighterSlug": "carlos-calderon",
            "fighterName": "Carlos Calderon",
            "corner": "blue"
          },
          {
            "fighterId": "997d121e-6d0b-4916-a8b7-2c69b359b41a",
            "fighterSlug": "austin-bashi",
            "fighterName": "Austin Bashi",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "austin-bashi",
        "method": "SUB",
        "methodDetails": "Rear Naked Choke",
        "resultRound": 1,
        "resultTime": "1:07"
      },
      {
        "boutId": "fight-night-november-07-2026-andre-petroski-mantas-kondratavicius",
        "cardPosition": "Prelims 6",
        "fighters": [
          {
            "fighterId": "3c4fbc90-5087-454a-80a0-82a53f381455",
            "fighterSlug": "andre-petroski",
            "fighterName": "Andre Petroski",
            "corner": "blue"
          },
          {
            "fighterId": "29d9fb0c-e709-4ac8-8e94-a9e31b854575",
            "fighterSlug": "mantas-kondratavicius",
            "fighterName": "Mantas Kondratavičius",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "andre-petroski",
        "method": "KO/TKO",
        "methodDetails": "Punches",
        "resultRound": 2,
        "resultTime": "4:39"
      },
      {
        "boutId": "fight-night-november-07-2026-seokhyeon-ko-wellington-turman",
        "cardPosition": "Prelims 7",
        "fighters": [
          {
            "fighterId": "33ec8329-09eb-4946-898f-1e0942efc5d8",
            "fighterSlug": "wellington-turman",
            "fighterName": "Wellington Turman",
            "corner": "blue"
          },
          {
            "fighterId": "370a18c8-977f-422e-a844-5d8ed65f6f50",
            "fighterSlug": "seokhyeon-ko",
            "fighterName": "Seokhyeon Ko",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "seokhyeon-ko",
        "method": "U-DEC",
        "methodDetails": null,
        "resultRound": 3,
        "resultTime": "5:00"
      },
      {
        "boutId": "fight-night-november-07-2026-jonny-parsons-jose-henrique",
        "cardPosition": "Prelims 8",
        "fighters": [
          {
            "fighterId": "4919764f-b74e-40ba-b57b-e3eeef512bdf",
            "fighterSlug": "jose-henrique",
            "fighterName": "José Souza",
            "corner": "blue"
          },
          {
            "fighterId": "8b348104-718f-41c5-9314-3f11096a09a7",
            "fighterSlug": "jonny-parsons",
            "fighterName": "Jonny Parsons",
            "corner": "red"
          }
        ],
        "outcome": "win",
        "winnerFighterSlug": "jose-henrique",
        "method": "KO/TKO",
        "methodDetails": "Punches",
        "resultRound": 1,
        "resultTime": "4:16"
      }
    ]
  }
}

Handler

Verify the signature on the raw bytes, answer fast, dedupe on the id. The same for every sport. PHP, Ruby and more.

Node / Express
JavaScript
// server.js  (npm install express)
import crypto from "node:crypto";
import express from "express";

const app = express();
const seen = new Set(); // use your database in production

// express.raw keeps the exact bytes. express.json() here would break the signature.
app.post("/webhooks/cito", express.raw({ type: "*/*" }), (req, res) => {
  const expected = crypto
    .createHmac("sha256", process.env.CITO_WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");
  const got = req.get("X-Cito-Signature") || "";
  const valid =
    got.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
  if (!valid) return res.sendStatus(401);

  res.sendStatus(200); // answer within 10 seconds, then work

  const { id, event, data } = JSON.parse(req.body.toString("utf8"));
  // The id is the same on every retry and replay. A replay you asked for gets through, so make the work safe to repeat.
  // test is true on the envelope of test deliveries: keep those out of your real data.
  if (seen.has(id) && !data.replayed) return;
  seen.add(id);

  switch (event) {
    case "cs2.match.completed":
      // handle cs2.match.completed
      break;
  }
});

app.listen(3000);
Next.js
TypeScript
// app/api/webhooks/cito/route.ts
import crypto from "node:crypto";

const seen = new Set<string>(); // use your database in production

export async function POST(req: Request) {
  const raw = await req.text(); // the exact body, before any JSON parsing
  const expected = crypto
    .createHmac("sha256", process.env.CITO_WEBHOOK_SECRET!)
    .update(raw, "utf8")
    .digest("hex");
  const got = req.headers.get("x-cito-signature") ?? "";
  const valid =
    got.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
  if (!valid) return new Response("invalid signature", { status: 401 });

  const payload = JSON.parse(raw);
  // The id is the same on every retry and replay. A replay you asked for gets through, so make handle() safe to repeat.
  // payload.test is true on test deliveries: keep those out of your real data.
  if (!seen.has(payload.id) || payload.data?.replayed) {
    seen.add(payload.id);
    // Do slow work in a queue. Answer within 10 seconds.
    handle(payload).catch(console.error);
  }
  return new Response("ok");
}

async function handle({ event, data }: { event: string; data: any }) {
  switch (event) {
    case "cs2.match.completed":
      // handle cs2.match.completed
      break;
  }
}
Python / FastAPI
Python
# main.py  (pip install fastapi uvicorn)
import hashlib, hmac, json, os
from fastapi import BackgroundTasks, FastAPI, HTTPException, Request

app = FastAPI()
SECRET = os.environ["CITO_WEBHOOK_SECRET"].encode()
seen = set()  # use your database in production

def handle(event, data):
    if event == "cs2.match.completed":
        pass  # handle cs2.match.completed

@app.post("/webhooks/cito")
async def cito_webhook(request: Request, tasks: BackgroundTasks):
    raw = await request.body()  # the exact bytes, before any JSON parsing
    expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected.encode(), request.headers.get("x-cito-signature", "").encode()):
        raise HTTPException(status_code=401, detail="invalid signature")

    payload = json.loads(raw)
    # The id is the same on every retry and replay. A replay you asked for gets through, so make handle() safe to repeat.
    # payload["test"] is True on test deliveries: keep those out of your real data.
    if payload["id"] not in seen or payload["data"].get("replayed"):
        seen.add(payload["id"])
        tasks.add_task(handle, payload["event"], payload["data"])  # runs after we answer
    return {"ok": True}
Python / Flask
Python
# app.py  (pip install flask)
import hashlib, hmac, json, os
from flask import Flask, abort, request

app = Flask(__name__)
SECRET = os.environ["CITO_WEBHOOK_SECRET"].encode()
seen = set()  # use your database in production

def handle(event, data):
    if event == "cs2.match.completed":
        pass  # handle cs2.match.completed

@app.post("/webhooks/cito")
def cito_webhook():
    raw = request.get_data()  # the exact bytes, before any JSON parsing
    expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected.encode(), request.headers.get("X-Cito-Signature", "").encode()):
        abort(401)

    payload = json.loads(raw)
    # The id is the same on every retry and replay. A replay you asked for gets through, so make handle() safe to repeat.
    # payload["test"] is True on test deliveries: keep those out of your real data.
    if payload["id"] not in seen or payload["data"].get("replayed"):
        seen.add(payload["id"])
        handle(payload["event"], payload["data"])  # move slow work to a queue: answer within 10 seconds
    return "", 200
Go
Go
// main.go
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"sync"
)

var (
	secret = []byte(os.Getenv("CITO_WEBHOOK_SECRET"))
	seen   sync.Map // use your database in production
)

type envelope struct {
	Event string          `json:"event"`
	ID    string          `json:"id"`
	Data  json.RawMessage `json:"data"`
}

func handle(event string, data json.RawMessage) {
	switch event {
	case "cs2.match.completed":
		// handle cs2.match.completed
	}
}

func citoWebhook(w http.ResponseWriter, r *http.Request) {
	raw, err := io.ReadAll(r.Body) // the exact bytes, before any JSON parsing
	if err != nil {
		http.Error(w, "bad body", http.StatusBadRequest)
		return
	}
	mac := hmac.New(sha256.New, secret)
	mac.Write(raw)
	expected := hex.EncodeToString(mac.Sum(nil))
	if !hmac.Equal([]byte(expected), []byte(r.Header.Get("X-Cito-Signature"))) {
		http.Error(w, "invalid signature", http.StatusUnauthorized)
		return
	}

	var p envelope
	if err := json.Unmarshal(raw, &p); err != nil {
		http.Error(w, "bad json", http.StatusBadRequest)
		return
	}
	// The id is the same on every retry and replay. A replay you asked for gets through, so make handle safe to repeat.
	// "test" is true on test deliveries: keep those out of your real data.
	var replay struct {
		Replayed bool `json:"replayed"`
	}
	_ = json.Unmarshal(p.Data, &replay)
	if _, dup := seen.LoadOrStore(p.ID, true); !dup || replay.Replayed {
		go handle(p.Event, p.Data) // answer first, work after
	}
	w.WriteHeader(http.StatusOK)
}

func main() {
	http.HandleFunc("/webhooks/cito", citoWebhook)
	log.Fatal(http.ListenAndServe(":3000", nil))
}
Was this page helpful?