Reference
Glossary
Every word the API, the console and these documents use in a particular way — defined once, and used identically in all three.
The model
- Observation
What one source said about one thing at one time — the evidence, never edited and never deleted.
An observation records a claim: source, type, external id, when it was observed, a confidence, and the data itself. Observations are append-only. Correcting the hub does not mean changing an observation; it means adding a new one from a source ranked high enough to win. Every payload is archived raw before anything parses it, so a run can be replayed after a parser improves.
- Canonical entity
The hub’s single answer for a real-world thing — derived from observations by policy, never hand-edited.
Canonical rows (a fixture, a team, a venue) are what consumers read. Each field holds the value from the observation that won it under the conflict policy, plus a provenance entry naming that observation. Nothing writes a canonical field directly — not an operator, not a support ticket.
- Provenance
Per field, the observation whose value won: which source, when it was observed, at what confidence.
Provenance is part of the public API, not an internal audit table. A fixture response carries a provenance object keyed by field path — start, venue.field — so “where did this kickoff time come from?” is answerable without asking anyone.
- Conflict policy
How a field is decided when sources disagree: authority rank first, then recency, then the source’s own confidence.
The policy runs per field, not per record — which is why a public source can fill in a field number the host left empty without touching the host’s kickoff time. A lower-ranked source never overwrites a higher-ranked one; it only fills gaps.
- Authority rank
The trust order over source classes, 1 (most authoritative) to 6. The lowest rank present wins the field.
Rank 1 authoritative push, 2 partner API, 3 host feed, 4 owner correction, 5 public source, 6 crowd. Rank is a property of the source class, fixed when the source is registered, and it is the only lever that settles a disagreement — there is no manual override.
See alsoSource classConflict policy
- Source class
What kind of source this is, which fixes its authority rank.
Authoritative push (the host platform pushing its own data), partner API (a platform the hub polls), host feed (iCal or CSV a club publishes), owner correction (a club admin fixing their own record), public source (a schedule its organiser publishes openly), crowd (a parent reporting a score).
See alsoAuthority rankSource
- Confidence
How sure the source is about this observation, 0 to 1. It breaks ties inside a rank, never across ranks.
Confidence is the source’s own claim about itself: a structural parser that read a clean table sends a high number; an extractor that inferred a kickoff from prose sends a low one. A confident public source still loses to a hesitant host push, because rank is checked first.
See alsoConflict policyAuthority rank
- Version
A counter on a canonical entity, incremented on every applied change — what change events and standings cite.
See alsoChanged fieldsDelta sync
- Changed fields
The field paths an applied change actually moved, carried on the change event so consumers can react narrowly.
See alsoDelta syncCloudEvent
Ingestion
- Source
A registered origin of observations, with a key, a class and therefore a rank.
Every observation names its source; a push from an unregistered source is refused with an unknown_source problem. Sources are per mode: a source key registered in test mode does not exist in live mode.
- Batch
One push of up to 1,000 observations, recorded with what was accepted, what was rejected, and where the raw payload was stored.
Validation is per observation: one malformed record costs that record, not the batch. The batch is accepted (202) if at least one observation is valid, and rejected outright only if none is. Processing is asynchronous — a 202 means stored and queued, not applied.
- Idempotency key
A caller-chosen string that makes a retry safe: replaying a batch with the same key returns the original receipt instead of applying twice.
Required on every push. Choose something derived from the payload or the job that produced it, not a fresh UUID per attempt — a new key on a retry defeats the purpose.
- Receipt
The 202 answer to a push: the batch id, how many observations were accepted, and the pointer and message for each one rejected.
See alsoBatchProblem document
- Raw payload
The unmodified bytes of everything ingested, archived to blob storage before any parsing.
Kept so that a parser fixed in March can be replayed over January. Replay is a designed property of the pipeline, not a rescue operation — which is why the batch row carries the blob path.
- Replay
Re-running an improved parser over payloads already archived, rather than re-fetching from the source.
See alsoRaw payloadSnapshot
- Feed
An iCal or CSV schedule the hub polls — or that you upload — registered with the things a calendar cannot say about itself.
A calendar file does not know its own sport, team or time zone, so those are supplied at registration. The hub polls politely: conditional requests, content hashing, backoff on failure. Everything fetched goes through the same validation a partner push does. A feed is a rank-3 source: evidence, not gospel.
- Connector
A versioned structural parser pointed at a published schedule, with run history, drift detection and an agreed request budget.
Connectors are the public-source tier. Every payload is snapshotted to the lake under the run id, and the parser reads the snapshot rather than the network, so any run can be replayed against a newer parser. Each source is read within a budget agreed with the site that publishes it.
- Parser
The named, versioned code that turns one fetched document into observations.
The version is recorded on every run, so “which parser produced this?” is answerable years later — and so a fix can be replayed over the snapshots the old version misread.
- Run
One execution of one feed or connector: when it started, how long it took, what it yielded, and any problems.
- Yield
How many observations a run produced. A yield that collapses against its own history is one of the signals drift detection watches.
- Drift
The source changed shape under the parser. The run’s output is held, never applied, until an operator fixes the parser or accepts the new shape.
Three signals raise it: the layout fingerprint changed, the run yielded nothing, or the yield collapsed against its own history. The ordering is the point — the hold happens before apply, so a site redesign costs a held run rather than a corrupted season.
- Layout fingerprint
A hash of the structural shape of a fetched page — what drift detection compares this run against the last.
See alsoDrift
- Accept new shape
Declaring the source’s new layout to be the baseline, which clears the hold and resumes the connector on its next run.
Accepting also discards the stored conditional-request validators; otherwise the next fetch answers 304 and the connector never sees the page it was just told to re-learn.
See alsoDriftLayout fingerprint
- Snapshot
The exact document a run fetched, stored in the lake under that run’s id.
Extraction may only read snapshots — it never fetches for itself. That rule is what makes a parser fix replayable over history instead of only forward.
See alsoRaw payloadReplayRun
- Catalog
A connector’s index of everything it could sync — the list of tournaments a site publishes, which becomes the denominator for coverage.
- Target
One thing inside a catalog that is kept in sync — one tournament out of thousands — with its own state, schedule and kill switch.
See alsoCatalogTarget stateCoverage
- Target state
Where one target stands, in a vocabulary that separates what is ours to fix from what is not.
Unpublished is decided before failing, deliberately: a catalog listing a tournament months before the host posts a schedule is the common case, and it is not a fault. Only stale and failing count as ours.
- Coverage
Per week: how many catalogued targets play, how many the host published, how many the hub pulled, and how many are fresh.
The catalog is the denominator, so the number cannot be flattered by ignoring what was never fetched. Published is the host’s doing; pulled and fresh are the hub’s. Fresh below published is the gap worth acting on.
See alsoCatalogTargetFreshness SLO
- Freshness SLO
How recently a target must have synced to count as current — tighter the closer its games are to being played.
See alsoCoverageTarget state
- Poll interval
How often the hub re-fetches a feed or target. Conditional requests make an unchanged fetch nearly free.
- Supply tier
How data arrives: push (a partner posts), pull (the hub polls an API), feed (iCal/CSV), public source (a schedule published openly), correction (an owner fixes their own record).
See alsoSource classConnectorFeed
Identity
- Identity resolution
Deciding which canonical entity an incoming observation is about, in four stages: known id, structured fuzzy match, embedding, threshold.
“Lakeside FC 2014B Blue” and “Lakeside 14B Blue” are one team, and two sources describing one game must become one fixture. Above the auto-merge threshold the external id is mapped onto the existing entity; in the review band a new entity is created and the pair waits for a human.
- External id
What one source calls a thing, in that source’s own namespace. A canonical entity lists every external id that resolved to it.
- Mapping
A row saying “this source’s external id means this canonical entity”. Merging repoints mappings; it never mutates a canonical row.
That is what makes a wrong merge one undo rather than an archaeology project: approval repoints mappings and sets a merged_into pointer, so reversing it restores exactly what was.
See alsoIdentity resolutionReview queue
- Review queue
Proposed merges the resolver scored below the auto-merge threshold, shown with both sides, the facts and the evidence per signal.
- Auto-merge threshold
The score above which a merge happens without asking. Set from a labelled evaluation set, not from taste.
A test fails the build if precision at this threshold ever drops below 1.0 on the labelled set. Below the threshold and above the review floor lies the review band.
See alsoReview queueEvidence
- Evidence
The per-signal breakdown behind a match score: name tokens, age group, gender, club, region, date and venue proximity, each with its own contribution.
Some signals are vetoes rather than weights. Sibling teams that differ only by colour — “2014B Blue” against “2014B White” — can never auto-merge however alike the rest looks.
See alsoReview queueAuto-merge threshold
The API
- Test and live mode
Two isolated partitions of every table. The API key decides which one a request touches; there is no mode parameter to get wrong.
A test key writes to a sandbox you can reset from the dashboard; a live key touches production data, which has no reset. Sources, feeds, keys, webhooks and data are all per mode — a source registered in test does not exist in live.
- Secret key
sk_test_… / sk_live_… — full authority: push, read, manage webhooks. Server-side only, shown once at creation.
Only a hash is stored, so a lost key is rolled, never recovered. Rolling takes effect immediately.
- Publishable key
pk_test_… / pk_live_… — read-only, public-tier, safe to ship inside a web page. What the calendar widget uses.
A publishable key that attempts a write gets a 403 with publishable_key as the problem type.
See alsoSecret keyGrant
- Test-mode reset
Wiping the test partition and starting again. Live data has no equivalent.
See alsoTest and live mode
- ETag
A version tag on a response. Send it back as If-None-Match and an unchanged answer costs a 304 and almost no bytes.
Caching is layered: Cloudflare at the edge, Redis behind it, ETags on the wire, delta sync for the client’s own store. Cache keys always include mode and version, and authenticated non-public data is never cached at the edge.
See alsoDelta syncCursor
- Cursor
An opaque bookmark into a stable ordering. Pass the one you were given to continue exactly where you stopped.
Cursors are keyset-based, not offsets, so a page never skips or repeats a row because something was inserted while you were reading. A cursor that no longer decodes returns invalid_cursor: start the page again without one.
See alsoDelta syncETag
- Delta sync
GET /v1/changes — every applied change in order from your stored cursor, so you never re-download a season.
Filtering by type narrows what is returned, but the cursor still advances past everything, so changing your filter never loses your place. has_more tells you to keep paging before you sleep.
- Problem document
The RFC 9457 shape every error uses: a stable type URI, a human detail, and for batches an errors array with the index and JSON pointer of each rejected observation.
See alsoReceipt
- Prefixed ULID
Every identifier says what it is: fx_ fixture, tm_ team, cmp_ competition, org_ organization, ven_ venue, src_ source, feed_, batch_, obs_.
The body is a ULID: sortable by creation time, safe to generate anywhere, no coordination required.
See alsoExternal id
- Grant
An organization’s permission for a named consumer to read data that is not public-tier — participant rosters, for instance.
Without a grant the response says what is being withheld rather than pretending it does not exist: player_lines_withheld on a box score is the shape of an honest refusal.
See alsoPublishable keyOrganization
- Usage
Calls by endpoint and mode over the period, with error counts and average latency — the same rollup billing would read.
See alsoTest and live mode
- Rate limit
The ceiling on requests per key. Exceeding it returns 429 with a problem document; back off and retry.
Events
- CloudEvent
The envelope every emitted event uses: specversion, type (oyss.*), subject (the entity id), time and data.
See alsoEvent typeWebhook subscription
- Event type
What happened, as a dotted name: oyss.fixture.rescheduled, oyss.result.finalized, oyss.team.merged. Subscriptions may use globs.
See alsoCloudEventWebhook subscription
- Outbox
Events written in the same transaction as the canonical change, then relayed separately — so an event is never emitted for a change that rolled back, or lost for one that committed.
See alsoCloudEventDispatch
- Dispatch
The relay that drains the outbox: purging caches and scheduling webhook deliveries, out of band and with per-subscription fairness.
Fairness is the point: one dead endpoint cannot delay anyone else’s events.
- Webhook subscription
A URL, the event types it wants, and an optional scope filter. The signing secret is returned once, at creation.
Treat webhooks as a nudge, not the ledger. Answer 2xx quickly, do the work asynchronously, and reconcile against the change stream from your stored cursor.
- Signing secret
The key every delivery is signed with, HMAC-SHA256 over the raw body. Verify before trusting a payload.
See alsoWebhook subscription
- Delivery attempt
One attempt to hand one event to one endpoint: status code, duration, error, and when the next attempt is due.
A failed delivery retries with exponential backoff for 24 hours. An endpoint that never answers is disabled automatically — no successful delivery for 30+ minutes across 20+ attempts — with the reason recorded.
- Auto-disable
Switching off an endpoint that has stopped answering entirely, so the queue is not held hostage by a dead URL.
Re-create the endpoint once it is healthy, then reconcile the gap with delta sync.
See alsoDelivery attemptDelta sync
- Redeliver
Sending an event again, on purpose, from the console — for an endpoint that was down or a consumer that lost the payload.
See alsoDelivery attempt
- Dead letter
A message that failed processing enough times to be set aside rather than retried forever, kept with its error for redrive.
See alsoRedrive
- Redrive
Putting dead-lettered messages back on the queue once the cause is fixed.
See alsoDead letter
Schedule data
- Fixture
Anything schedulable: a game, practice, tryout, scrimmage or team event.
See alsoKindFixture statusTime status
- Kind
What sort of fixture this is — game, practice, tryout, scrimmage or event. Non-games carry a title instead of two sides.
See alsoFixture
- Fixture status
Where a fixture stands: scheduled, in progress, completed, postponed, cancelled or forfeit.
A cancellation is a status change, never a delete. A family that saw the game yesterday needs to see the cancellation today, so cancelled fixtures stay readable and are struck through rather than hidden.
See alsoFixtureTime status
- Time status
How much to trust the clock: confirmed, estimated, or tbd for a fixture that has a date but no time yet.
A tbd fixture carries a date instead of a start instant, which is the honest shape — inventing a midnight kickoff to satisfy a timestamp column is how a schedule lies.
- Time zone
All instants are UTC; the venue carries an IANA zone so a local kickoff time can be shown correctly.
See alsoTime statusVenue
- Competition
A tournament, league or season — the container a division and its stages belong to.
- Division
The bracket inside a competition, structured rather than named: age group, gender, level.
Structured because “2014 Boys Premier” and “U11B Premier” are the same division, and a string comparison will never know that.
See alsoCompetitionStage
- Stage
A phase within a division — pool play, a bracket, a regular season. Standings are computed per stage.
- Venue
Where a fixture is played, with an optional field or pitch within it and an IANA time zone.
See alsoTime zone
- Side
A team’s part in a fixture, with a role: home, away, or participant when the format has no home side.
See alsoFixture
- Organization
The club, league or governing body a team belongs to — and the party that grants access to its non-public data.
See alsoGrant
- Standings
A stage’s table, computed from results and the stage’s own rules. Never edited: a wrong table means a wrong result.
Recomputed in the same transaction that lands a final result, which then emits oyss.standing.updated. Given the results and the rules there is exactly one right table.
See alsoTiebreakerTraceStage
- Tiebreaker
The stage’s ordered rules for separating level teams — head-to-head, goal difference, goals for, and so on.
The rules are the stage’s, not the hub’s: the competition observation carries the points scheme and an ordered tiebreaker list. Unknown rule names are rejected at ingest rather than silently skipped. Where a host never published rules, a conservative default applies and the response says so.
- Trace
The ordered rules that placed one row where it is, returned with the row — so “why are they second?” is answerable from the response.
See alsoStandingsTiebreaker
- Box score
Per-fixture statistics in two tiers: team lines, which are public, and player lines, which need a grant.
Without the grant the response says player_lines_withheld rather than quietly returning less.
- Stat line
One row of statistics for one team or player in one fixture, in that sport’s schema.
See alsoBox score
Accounts
- Account
A partner: an organization, its people, its keys, its sources and its usage.
See alsoOperatorOrganization
- Operator
A Sideline role that sees the whole network — connectors, the review queue, the accounts directory — rather than one account.
The navigation follows the role; the management API enforces it per endpoint regardless of what the navigation shows.
See alsoAccount
- Session
One signed-in browser, listed with when it was created and last seen, and revocable on its own.
- Suspend
Switching an account off with a recorded reason: its keys stop working immediately and the reason is shown to its users.
See alsoAccount
Values
The strings a status or a class can actually be, and what each one means. These are the same sets the console renders in a badge — hovering one there says exactly what you read here.
Batch status
batch.status
How far a pushed batch has got. A batch is stored and acknowledged before it is applied, so received is a normal answer for a batch that arrived a second ago.
| Value | Means |
|---|---|
received | Stored and queued. The raw payload is safe; nothing has been applied yet. |
processing | A worker is resolving and applying its observations right now. |
done | Every accepted observation was resolved and applied. |
failed | Processing stopped on an error. The error is on the batch, and the raw payload can be replayed once the cause is fixed. |
Feed and connector status
feed.status
The verdict on the last run. drifted is the one that stops the pipeline: output is held rather than applied.
| Value | Means |
|---|---|
pending | Registered but never fetched. It runs at its next scheduled poll, or immediately if you refresh it. |
healthy | The last run fetched and applied cleanly. |
degraded | The last run parsed, but the parser had something to say about it — some rows were skipped or incomplete. |
failing | The last run could not be fetched, or could not be read at all. The error is on the row. |
disabled | Turned off — by you, or by the runtime after repeated failure. It will not be polled. |
drifted | The page changed shape or the yield collapsed, so the run’s output is held. Nothing was applied: fix the parser, or accept the new shape. |
Target state
target.state
Where one target inside a catalog stands. The vocabulary separates what is the hub’s to fix — stale and failing — from what is simply the world being itself.
| Value | Means |
|---|---|
discovered | In the catalog, never successfully fetched. Waiting for its first sync. |
unpublished | The catalog lists it, but the host has not posted a schedule yet. Not ours to fix, and not a fault. |
syncing | Fetched, and inside the freshness SLO for how close its games are to being played. |
stale | The last successful sync is older than the SLO allows. Ours to fix. |
failing | Consecutive failures against a target that used to work. Ours to fix. |
dormant | Finished and settled; nothing more will change. |
excluded | The publisher’s terms, or an operator’s kill switch, says leave this one alone. The reason is recorded on the row. |
Source class and rank
source.class
What a source is, and therefore how far it is trusted. Rank 1 wins every field it has an opinion about; rank 6 fills gaps nobody else filled.
| Value | Means |
|---|---|
authoritativePush1 · Authoritative push | The platform that runs the competition, pushing its own data. |
partnerApi2 · Partner API | A platform’s API the hub polls with its blessing. |
hostFeed3 · Host feed | An iCal or CSV a club or host publishes. Evidence, not gospel. |
ownerCorrection4 · Owner correction | A club admin fixing their own record — an observation, not a hand-edit. |
scrape5 · Public source | A schedule its organiser publishes openly, read as evidence. Fills gaps; never overwrites a ranked source. |
crowd6 · Crowd | A parent reporting a score. Visible, attributed, and outranked by everything. |
Fixture status
fixture.status
Nothing here is a delete. A cancelled game stays readable, because the family that saw it yesterday needs to see the cancellation today.
| Value | Means |
|---|---|
scheduled | On the calendar and expected to be played. |
inProgressin_progress | Being played now. |
completed | Played. A final result may or may not have been reported yet. |
postponed | Not played at the scheduled time; a new time may follow as a reschedule. |
cancelled | Called off. The fixture stays in the API and in calendars, struck through rather than removed. |
forfeit | Awarded without being played. Standings count it like any other result. |
Time status
fixture.time_status
How much to trust the clock on a fixture. Honesty here is what keeps a calendar from lying.
| Value | Means |
|---|---|
confirmed | The source published this exact kickoff time. |
estimated | Derived rather than published — a bracket slot, a typical start. Treat it as approximate. |
tbd | The date is known, the time is not. The fixture carries a date instead of a start instant. |
Fixture kind
fixture.kind
Not everything on a team’s calendar is a game, and pretending otherwise breaks the calendar.
| Value | Means |
|---|---|
game | A competitive match, with two sides and a result. |
practice | A training session. One side, a title, no result. |
scrimmage | A friendly. Two sides, usually no standings impact. |
tryout | A selection session. |
event | Anything else on the calendar — a team meeting, a photo day. |
Review status
review.status
A proposed merge the resolver would not make on its own.
| Value | Means |
|---|---|
pending | Waiting for a decision. Both entities exist separately until then. |
approved | Merged: the mappings were repointed and a merged_into pointer set. One undo reverses it exactly. |
rejected | Left apart. The pair will not be proposed again on the same evidence. |
API key kind
key.kind
Two kinds of key, and the difference is where they are allowed to live.
| Value | Means |
|---|---|
secretsk_test_… / sk_live_… | Full authority: push, read, manage webhooks. Server-side only. Shown once; only a hash is kept. |
publishablepk_test_… / pk_live_… | Read-only, public tier, safe inside a web page. A write attempt returns 403 publishable_key. |
Supply tier
connector.tier
How the data got here. The tier decides which console page a source appears on, and how much it is trusted.
| Value | Means |
|---|---|
push | A partner posts observations to the hub. The most authoritative route. |
pull | The hub polls a partner’s API on a schedule. |
feed | An iCal or CSV file, fetched by URL or uploaded. |
scrapePublic source | A schedule published openly by its organiser, read by a versioned connector with validation in front of it. |
correction | An owner fixing their own record, as an observation from a ranked source. |
Account status
account.status
Whether an account’s keys work.
| Value | Means |
|---|---|
active | Normal. Keys work in both modes. |
suspended | Switched off with a recorded reason. Keys stop working immediately and the reason is shown to the account’s users. |
User role
user.role
What one person may do inside an account. Separate from the operator role, which is Sideline’s own.
| Value | Means |
|---|---|
owner | Everything, including billing, keys and inviting people. |
developer | Keys, webhooks, data and the explorer. Not account administration. |
viewer | Read-only: the data and the logs, no keys and no changes. |