TANITARDevelopers
WebhooksEvent catalogTournaments

tournament.tournament.placements-finalized

Version 1 of the tournament.tournament.placements-finalized webhook event.

A tournament completed and its final stage's placements are settled. Each placement carries the enrollment's entrant identity so an award consumer can resolve recipients without reading tournament storage: an opaque entrant's externalId is the tenant's own player identifier, while a saas principal is a customer-plane identity that the consumer must resolve explicitly rather than assume.

Event typetournament.tournament.placements-finalized
Schema version1
Routing keyevent.tournament.tournament.placements-finalized.v1
Produced byTournament Management
Schemaurn:defendr:contract:tournament:tournament-placements-finalized:1

data fields

FieldTypeRequiredDescription
tournamentIdstringyes
organizationIdstringyes
revisionintegeryes
stageIdstring or nullyes
completedAtstring (date-time)yes
placementsarray of objectyes
placements[].rankFromintegeryes
placements[].rankTointegeryes
placements[].rankinteger or nullyes
placements[].status"provisional", "final"yes
placements[].reason"champion", "runner_up", "consolation_winner", "consolation_loser", "eliminated", "standing"yes
placements[].enrollmentIdstringyes
placements[].entrantKeystringyes
placements[].entrantType"solo", "team"yes
placements[].teamIdstring or nullyes
placements[].entrantobjectyes
placements[].entrant.identityKeystringyes
placements[].entrant.mode"saas", "opaque"yes
placements[].entrant.principalIdstring or nullyes
placements[].entrant.externalIdstring or nullyes
placements[].rosterarray of valuesyes
placements[].roster[].identityKeystringyes
placements[].roster[].mode"saas", "opaque"yes
placements[].roster[].principalIdstring or nullyes
placements[].roster[].externalIdstring or nullyes
placements[].roster[].playerType"main", "substitute"yes

Some fields are required only in particular states. The JSON Schema below is authoritative.

Example delivery

Signed with the example secret whsec_placeholder_example_webhooks_signing_secret, so you can check your verifier against it. See Verifying signatures.

POST /your/webhook/endpoint HTTP/1.1
content-type: application/json
webhook-id: whd_00000000-0000-4000-8000-000000000080
webhook-timestamp: 1800000000
webhook-signature: v1,XvONMyH1FzH6+W7sEt+RfIo1vMru+JlrEVIv0l4O15g=
tanitar-event-type: tournament.tournament.placements-finalized
tanitar-schema-version: 1
tanitar-delivery-attempt: 1

{"messageId":"evt_00000000-0000-4000-8000-000000000080","kind":"event","type":"tournament.tournament.placements-finalized","schemaVersion":1,"occurredAt":"2027-01-15T10:00:00.000Z","producer":"tournament-management-service","accountId":"acc_11111111-1111-4111-8111-111111111111","correlationId":"corr_00000000-0000-4000-8000-000000000080","data":{"tournamentId":"trn_example","organizationId":"org_example","revision":9,"stageId":"stg_final","completedAt":"2026-09-10T18:00:00.000Z","placements":[{"rankFrom":1,"rankTo":1,"rank":1,"status":"final","reason":"champion","enrollmentId":"enr_one","entrantKey":"opaque:player-one","entrantType":"solo","teamId":null,"entrant":{"identityKey":"opaque:player-one","mode":"opaque","principalId":null,"externalId":"player-one"},"roster":[]},{"rankFrom":2,"rankTo":2,"rank":2,"status":"final","reason":"runner_up","enrollmentId":"enr_two","entrantKey":"saas:prn_two","entrantType":"team","teamId":"team_two","entrant":{"identityKey":"saas:prn_two","mode":"saas","principalId":"prn_two","externalId":null},"roster":[{"identityKey":"opaque:player-three","mode":"opaque","principalId":null,"externalId":"player-three","playerType":"main"}]}]}}

The same body, formatted:

{
  "messageId": "evt_00000000-0000-4000-8000-000000000080",
  "kind": "event",
  "type": "tournament.tournament.placements-finalized",
  "schemaVersion": 1,
  "occurredAt": "2027-01-15T10:00:00.000Z",
  "producer": "tournament-management-service",
  "accountId": "acc_11111111-1111-4111-8111-111111111111",
  "correlationId": "corr_00000000-0000-4000-8000-000000000080",
  "data": {
    "tournamentId": "trn_example",
    "organizationId": "org_example",
    "revision": 9,
    "stageId": "stg_final",
    "completedAt": "2026-09-10T18:00:00.000Z",
    "placements": [
      {
        "rankFrom": 1,
        "rankTo": 1,
        "rank": 1,
        "status": "final",
        "reason": "champion",
        "enrollmentId": "enr_one",
        "entrantKey": "opaque:player-one",
        "entrantType": "solo",
        "teamId": null,
        "entrant": {
          "identityKey": "opaque:player-one",
          "mode": "opaque",
          "principalId": null,
          "externalId": "player-one"
        },
        "roster": []
      },
      {
        "rankFrom": 2,
        "rankTo": 2,
        "rank": 2,
        "status": "final",
        "reason": "runner_up",
        "enrollmentId": "enr_two",
        "entrantKey": "saas:prn_two",
        "entrantType": "team",
        "teamId": "team_two",
        "entrant": {
          "identityKey": "saas:prn_two",
          "mode": "saas",
          "principalId": "prn_two",
          "externalId": null
        },
        "roster": [
          {
            "identityKey": "opaque:player-three",
            "mode": "opaque",
            "principalId": null,
            "externalId": "player-three",
            "playerType": "main"
          }
        ]
      }
    ]
  }
}

JSON Schema

urn:defendr:contract:tournament:tournament-placements-finalized:1
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:defendr:contract:tournament:tournament-placements-finalized:1",
  "title": "Tournament placements finalized event payload",
  "description": "A tournament completed and its final stage's placements are settled. Each placement carries the enrollment's entrant identity so an award consumer can resolve recipients without reading tournament storage: an `opaque` entrant's externalId is the tenant's own player identifier, while a `saas` principal is a customer-plane identity that the consumer must resolve explicitly rather than assume.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "tournamentId",
    "organizationId",
    "revision",
    "stageId",
    "completedAt",
    "placements"
  ],
  "properties": {
    "tournamentId": {
      "type": "string",
      "minLength": 1
    },
    "organizationId": {
      "type": "string",
      "minLength": 1
    },
    "revision": {
      "type": "integer",
      "minimum": 1
    },
    "stageId": {
      "type": [
        "string",
        "null"
      ]
    },
    "completedAt": {
      "type": "string",
      "format": "date-time"
    },
    "placements": {
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "rankFrom",
          "rankTo",
          "rank",
          "status",
          "reason",
          "enrollmentId",
          "entrantKey",
          "entrantType",
          "teamId",
          "entrant",
          "roster"
        ],
        "properties": {
          "rankFrom": {
            "type": "integer",
            "minimum": 1
          },
          "rankTo": {
            "type": "integer",
            "minimum": 1
          },
          "rank": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "status": {
            "enum": [
              "provisional",
              "final"
            ]
          },
          "reason": {
            "enum": [
              "champion",
              "runner_up",
              "consolation_winner",
              "consolation_loser",
              "eliminated",
              "standing"
            ]
          },
          "enrollmentId": {
            "type": "string",
            "minLength": 1
          },
          "entrantKey": {
            "type": "string",
            "minLength": 1
          },
          "entrantType": {
            "enum": [
              "solo",
              "team"
            ]
          },
          "teamId": {
            "type": [
              "string",
              "null"
            ]
          },
          "entrant": {
            "$ref": "#/$defs/identity"
          },
          "roster": {
            "type": "array",
            "items": {
              "$ref": "#/$defs/rosterMember"
            }
          }
        }
      }
    }
  },
  "$defs": {
    "identity": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "identityKey",
        "mode",
        "principalId",
        "externalId"
      ],
      "properties": {
        "identityKey": {
          "type": "string",
          "minLength": 1
        },
        "mode": {
          "enum": [
            "saas",
            "opaque"
          ]
        },
        "principalId": {
          "type": [
            "string",
            "null"
          ]
        },
        "externalId": {
          "type": [
            "string",
            "null"
          ]
        }
      }
    },
    "rosterMember": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "identityKey",
        "mode",
        "principalId",
        "externalId",
        "playerType"
      ],
      "properties": {
        "identityKey": {
          "type": "string",
          "minLength": 1
        },
        "mode": {
          "enum": [
            "saas",
            "opaque"
          ]
        },
        "principalId": {
          "type": [
            "string",
            "null"
          ]
        },
        "externalId": {
          "type": [
            "string",
            "null"
          ]
        },
        "playerType": {
          "enum": [
            "main",
            "substitute"
          ]
        }
      }
    }
  }
}

On this page