Sideline Exchange/
Dashboard →

Consume

Results and standings

Every reported score with its source, and deterministic tables that explain themselves.

A result is an observation like everything else: sources report scores, the conflict policy picks a winner by rank, and the losers stay visible. GET /v1/fixtures/{id}/results returns every report with its source and marks the one that won — when two scorers kept two books, the disagreement is information, not a bug.

Standings are computed, never edited

When a final result lands on a fixture in a pool or league stage, the table recomputes in the same transaction and oyss.standing.updated fires. Given the results and the rules there is exactly one right table — a wrong table is a wrong result, and a wrong result is corrected by an observation from a ranked source.

The rules are the stage’s, not ours: the competition observation carries points ("3-1-0" is common, not universal) and an ordered tiebreakers list. Unknown rule names are rejected at ingest rather than silently skipped. When a host never published rules, a conservative default applies and the response says so (tiebreak_source: "default").

RuleMeaning
pointsPoints under the stage’s scheme; always applied first
head_to_head:mini_tablePoints recomputed among the tied teams only
head_to_head:pairwise_onlyHead-to-head, but only when exactly two teams are tied
goal_diff / goal_diff_capped:NGoal difference, optionally capped at N per game
goals_for / goals_against / winsThe usual columns
coin_tossNot computable: the tie stands, marked, until decided

Honesty about partial data is part of the contract: complete is false while any game lacks a final result, missing_fixtures counts them, provisional positions are flagged, and every row carries a trace — the ordered rules that placed it — so "why is Riverbend second?" is answerable from the response. derived_from names the exact fixture versions the table came from.

The exact shape of this