TANITARDevelopers
GuidesTournaments

Results and evidence

Report, correct and revert match results, read result history, handle forfeits, collect and review evidence, purge evidence media, and read standings.

A match result is written once and then corrected or reverted as needed. Every write is kept as a revision. Every write recomputes later bracket matches, standings, stage completion and tournament placements in the same transaction.

Report a result

PUT /matches/:matchId/result (results:mutate, Idempotency-Key required) records the outcome of a match that is ready, playing or already completed.

FieldMeaning
expectedRevisionThe match's current revision. A stale value is refused with 409 RESULT_STALE.
submissionKeyYour identifier for this submission. Resending the same key returns the original outcome and changes nothing, even if the match has moved on since.
entriesOne entry per occupant: entrantKey, plus score (score and sets scoring) or rank (rank scoring).
curl -X PUT "$TOURNAMENT_API_URL/matches/$MATCH_ID/result" \
  -H "Authorization: Bearer $TANITAR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"expectedRevision":3,"submissionKey":"final-report-1","entries":[{"entrantKey":"ent_a","score":2},{"entrantKey":"ent_b","score":1}]}'

The answer carries the updated match, the new result revision, the operation performed (apply for the first result, correct for any later one) and the cascade: its cascadeId, the affectedMatchIds, and whether standings were recomputed.

Rules the result must satisfy:

  • The entries must name exactly the match's occupants and fit its scoring mode (RESULT_ENTRANTS_INVALID, RESULT_ENTRIES_INVALID, RESULT_ENTRIES_INCOMPLETE).
  • A match that feeds another match cannot end in a draw (RESULT_DRAW_NOT_PROPAGABLE).
  • When the match requires evidence, an approved piece of evidence must exist first (RESULT_EVIDENCE_REQUIRED).
  • A pending or dormant match has no result yet (RESULT_MATCH_NOT_READY).

The first result publishes tournament.match.result-applied; a correction publishes tournament.match.result-corrected. Both are followed by tournament.standings.updated.

Correct a result

To correct a completed match, call PUT /matches/:matchId/result again with the match's current revision, a new submissionKey and the right entries. The previous revision is superseded, and everything downstream is recomputed: a later match whose slot changes has its own result invalidated and its occupants replaced, and a stage or tournament that was complete can reopen.

Revert a result

POST /matches/:matchId/result/revert clears the active result of a completed match and returns it to ready for replay.

FieldMeaning
expectedRevisionThe match's current revision.
submissionKeyResending the same key replays the original revert.
reasonOptional, up to 500 characters.

Dependent slots empty out, their results are invalidated, and standings and placements recompute. A match with no active result is refused with RESULT_NOT_APPLIED. The revert publishes tournament.match.result-reverted.

Result history

GET /matches/:matchId/result-revisions (results:read) returns every revision of the match in order, without pagination. Each revision records its operation (apply, correct, revert, or invalidate when an upstream correction voided it), its origin (participant for a result you reported, system for one Tanitar applied), the outcome, the cascadeId, who applied it and when, and which revision superseded it.

Forfeits

When a tournament sets lifecyclePolicy.matchForfeitTimeoutMinutes, each match starts a forfeit deadline when it becomes ready. At the deadline, if the match is still ready:

Situation at the deadlineOutcome
Every occupant marked readyNothing happens; the forfeit is cancelled.
One of two occupants marked readyThat occupant wins. Tanitar applies the result with origin system, without requiring evidence.
Anything elseThe forfeit expires unresolved; you report the result yourself.

Each resolution publishes tournament.match.forfeit-resolved with its resolution (applied or expired_unresolved) and, when applied, the winnerEnrollmentId. Reporting a result before the deadline cancels the forfeit. See match readiness.

Evidence

Evidence is a screenshot, a recording or another file an entrant submits to back a result. It is required before a result is accepted when the match has evidenceRequired (set through the tournament's resultPolicy). Submitting evidence acts for an entrant and needs participants:mutate; reviewing and purging it needs results:mutate; reading it needs results:read.

Upload a file

Files go straight to storage through a short-lived upload link; they never pass through the API.

  1. Create an upload intent with POST /matches/:matchId/evidence/uploads:

    FieldMeaning
    submittedByEntrantKeyThe entrant submitting. It must occupy the match.
    contentTypeThe file's media type; types your deployment does not accept are refused with EVIDENCE_CONTENT_TYPE_INVALID.
    contentLengthSize in bytes; above the limit is refused with EVIDENCE_UPLOAD_TOO_LARGE.
    checksumSha256Hex SHA-256 of the file.
    originalFilenameThe file's name.
    setNumberOptional: the game or set this evidence covers.

    The answer gives the uploadId, the uploadUrl, the requiredHeaders to send with it, the maximumSize and the link's expiresAt.

  2. PUT the file to uploadUrl with exactly the requiredHeaders, before expiresAt.

  3. Complete it with POST /matches/:matchId/evidence/uploads/:uploadId/complete. Tanitar verifies the stored object against the declared size and checksum and creates the evidence in pending. A mismatch is refused with EVIDENCE_STORAGE_VERIFICATION_FAILED, an expired upload with EVIDENCE_UPLOAD_EXPIRED. Completing twice returns the same evidence.

An upload that is never completed is cleaned up automatically after it expires. To discard one straight away, call DELETE /matches/:matchId/evidence/uploads/:uploadId (results:mutate).

To replace the file of a pending piece of evidence, run the same two steps on POST /matches/:matchId/evidence/:evidenceId/uploads and POST /matches/:matchId/evidence/:evidenceId/uploads/:uploadId/complete. Only the original submitter can replace it.

Read, edit and withdraw

RouteScopeWhat it does
GET /matches/:matchId/evidenceresults:readLists the match's evidence, optionally filtered by state (pending, approved, rejected, withdrawn); withdrawn evidence is left out unless you ask for it. No pagination.
GET /matches/:matchId/evidence/:evidenceIdresults:readReads one.
GET /matches/:matchId/evidence/:evidenceId/media-urlresults:readReturns a short-lived download url for the current file, with its contentType, contentLength, checksumSha256 and expiresAt. Withdrawn evidence has none.
PATCH /matches/:matchId/evidence/:evidenceIdparticipants:mutateChanges the setNumber of pending evidence.
DELETE /matches/:matchId/evidence/:evidenceIdparticipants:mutateWithdraws pending evidence.

POST /matches/:matchId/evidence with a mediaReference link records evidence hosted elsewhere instead of uploaded. It is accepted only where your deployment enables external links and only for allowed hosts; prefer uploads.

Review

POST /matches/:matchId/evidence/:evidenceId/review (results:mutate) takes a decision of approved or rejected. Only pending evidence can be reviewed. The review publishes tournament.match.evidence-reviewed; an approved piece unblocks a result on a match that requires evidence.

Purge media

Evidence keeps each file it has ever held. To delete a replaced file or a file from rejected or withdrawn evidence permanently, call DELETE /matches/:matchId/evidence/:evidenceId/media/:mediaId (results:mutate). The current file of pending or approved evidence cannot be purged (409 EVIDENCE_MEDIA_CURRENT).

If storage cannot delete the file, the media is marked purge-failed, the call answers 503 EVIDENCE_STORAGE_UNAVAILABLE, and tournament.match.evidence-media-purge-failed is published. Retry with POST /matches/:matchId/evidence/:evidenceId/media/:mediaId/retry-purge. Purging a file that is already purged succeeds and changes nothing. A completed purge publishes tournament.match.evidence-media-purged.

Standings

GET /tournaments/:slug/standings (results:read) returns the computed standings, optionally for one stageId, without pagination. Each row belongs to a stage and group and carries the entrant's played, wins, losses, draws, points, gamesWon, gamesLost, scored, conceded, pointDifference, its rank (or rankFrom/rankTo for a shared rank), the tiebreakApplied, and the computedFromCascadeId of the result write that produced it.

Standings are recomputed with every result, correction and revert; you never trigger them. Listen for tournament.standings.updated rather than polling. The ranking follows the tournament's resultPolicy (rankBy and tiebreakers) or the stage's standingPolicy.

See the event catalog for the full payload of every event on this page.

On this page