Delivery contract — Game Value, team board
The reference for every call. If you are integrating for the first time, read onboarding first: it puts these calls in order and tells you how to know each one worked.
GET /api/tasks/game-value-team/fixtures the schedule, with deadlines public
GET /api/tasks/game-value-team/teams the team registry public
POST /api/tasks/game-value-team/values deliver values Bearer <key>
GET /api/tasks/game-value-team/status what you still owe Bearer <key>
Contract version in force: 2026-09-game-value-team-v0. It is checked on every delivery.
Why you deliver, and nothing calls you
Participation is submission mode: your scheduled job posts values to the Arena. There is no endpoint for you to expose, nothing to keep running at our convenience, and no timeout of ours that can turn your infrastructure into part of your score.
What a live handshake would have proved — that your integration works end to end — is proved better by certification, which runs a complete round through the real checks before anything counts.
The fixture list
GET /api/tasks/game-value-team/fixtures?since=<iso>&competition=<name>
Public and unauthenticated. It is the single source of truth your job reads every run: postponements, rescheduled kick-offs and awarded results appear as they happen, and every match carries the deadline its round is due at before the match is played, so one call plans your week.
| Field | What it is |
|---|---|
match_id | The Arena's identifier, fam_<season>_<competition>_<n>. What values are delivered against, and stable from publication to scoring through any reschedule. |
competition, season, round | Where the fixture sits. |
kickoff | Europe/Paris, with its offset. |
home_team_id, away_team_id | Arena team ids, with home_team_name and away_team_name beside them for reading. |
status | scheduled, played, postponed, abandoned or awarded. |
deadline | When that round's values are due and freeze. |
updated_at | When this record last changed. What ?since= filters on. |
Integrate on the ids. Names are published so a row is readable and are not accepted in a delivery: a name match is a guess, and a guess attaches a value to the wrong club silently.
?since= takes the largest updated_at you already hold — those are UTC with microseconds, e.g.
2026-09-12T14:13:21.105435+00:00; a plain date works too — and returns only what changed since.
Omit it on a first run to read the whole season.
?competition= takes the exact published name: Premier League, LaLiga, Serie A, Bundesliga
or Ligue 1. Anything else is refused with the valid names, rather than answered with an empty
list that looks like a season with no football in it.
What each status means for you
status | Deliverable | Meaning |
|---|---|---|
scheduled | No | Not played yet. Includes a match currently in progress: it is not over, so it cannot be valued. |
played | Yes | Played and its result validated. |
awarded | Yes | Decided off the pitch. Values are stored like any other and the fixture is never scored. |
postponed | No | Not being played on this date. It keeps its match_id and returns with a new kick-off and its own deadline. |
abandoned | No | Started and not completed. |
The team registry
GET /api/tasks/game-value-team/teams
Every team with a fixture this season: team_id, club name, country and competition. Read it once,
map each team_id to your own identifier, and keep the mapping. No provider identifiers appear here
or anywhere else in this benchmark.
Delivering values
POST /api/tasks/game-value-team/values Authorization: Bearer <your key>
POST /api/tasks/game-value-team/values?dry_run=true
{
"task": "game_value_team",
"contract_version": "2026-09-game-value-team-v0",
"model_version": "my-model-1.4",
"values": [
{"match_id": "fam_2627_premierleague_0042", "team_id": "FAT0000042", "value": 1.83},
{"match_id": "fam_2627_premierleague_0042", "team_id": "FAT0000117", "value": 1.21}
]
}
All fields are required. model_version is yours to choose and is recorded with the delivery, so a
score can always be attributed to the model that produced it.
One number per team per match: what that team's performance in that match was worth.
- On your own scale. Nothing is normalised and there are no units to agree on, because the score depends only on the order your values put fixtures in.
- Both sides of a fixture must travel together. A fixture priced on one side has no net value for either team — and a participant able to describe half a match could shape a rival's signal by choosing which halves to send.
- Played matches only, as the table above defines it.
- Any number of matches per delivery, from one round or several.
The receipt
The response is the receipt, and it is what you keep.
{
"receipt_id": "rcpt_2026-09-22T04-05-11Z_0007_8ecde1",
"received_at": "2026-09-22T04:05:11.318402+00:00",
"payload_sha256": "1304bb09…",
"dry_run": false,
"certified": false,
"summary": {"accepted": 18, "replaced": 2, "unchanged": 0, "late": 0, "rejected": 0},
"entries": [{"match_id": "…", "team_id": "…", "outcome": "accepted", "deadline": "…"}]
}
received_at is the Arena's clock; your own timestamps are not read, so a wrong clock on your
side cannot move a deadline. payload_sha256 is taken over the body exactly as received: check it
against your own and you have proof of what you sent, not a claim about it.
One bad entry never costs the others. Whole-delivery problems are refused before any entry is judged; everything else is per entry.
| Outcome | Means |
|---|---|
accepted | Stored. Nothing was held for this team in this match. |
replaced | Stored. It supersedes a different value you sent before the deadline. |
unchanged | The same value you already held. Stored state is untouched — and this is allowed after the deadline, because resending the same number is not a change. |
late | Stored. Your first value for this team in this match, arriving after the deadline. It is kept and it is not scored. |
rejected | Not stored, with a reason. |
Rounds, deadlines and the freeze
This is the mechanic the benchmark turns on, and the one worth reading twice.
When values are due
A round's values are due at 23:59 Europe/Paris, a full day after its last match.
| The round's last match is played on | Values are due |
|---|---|
| Friday, Saturday, Sunday or Monday | Tuesday 23:59 |
| Tuesday, Wednesday or Thursday | 23:59 the next day |
A full day, because match data is not final at the whistle. A weekend round is due on the Tuesday whether or not it has a Monday match, so you have one deadline a weekend rather than one per match.
A postponed match is a round of its own. When a match is moved more than five days from the rest
of its round, it stops sharing their deadline and carries one derived from the date it is actually
played — with its own delivery budget, so it does not spend one its original round already spent.
Every fixture carries its deadline in the fixture list, so your job never computes this itself.
Replaceable until the deadline, frozen at it
Before the deadline you may send a value again and the last one counts. That window is what the extra day is for: correct a value as the match data settles.
After the deadline:
- a first value for that team in that match still lands, marked
late, and is not scored; - the same value again is
unchangedand harmless; - a different value is rejected as
frozen.
Every version is kept either way, so which one counted is readable rather than asserted.
What a value counts toward
A value counts only toward fixtures that kick off after its deadline.
This is the rule that makes the benchmark a forecast rather than a description. The one-day window means a value may legitimately arrive after the team's next match has been played — and a value that could be written knowing a result must never be allowed to predict it.
The practical consequence: delivering history after the fact achieves nothing. Values for matches that kicked off before you certified are refused outright, and a value delivered late is stored but never scored.
Three deliveries per round
You may store three deliveries per round: a first and two corrections. It is also loop protection — a job stuck resending stops rather than filling the record.
Counted per round, not per match: one delivery carrying four matches of the same round spends one of its three. A delivery spanning several rounds spends one from each. Dry runs are exempt and count toward nothing.
What you still owe
GET /api/tasks/game-value-team/status Authorization: Bearer <your key>
{
"as_of": "2026-09-22T04:06:00+00:00",
"certified": true,
"due": [
{
"competition": "Premier League",
"round": 6,
"deadline": "2026-09-22T23:59:00+02:00",
"expected": 20,
"delivered": 18,
"deliveries_left": 2,
"missing": [{"match_id": "fam_2627_premierleague_0104", "team_id": "FAT00066"}]
}
],
"missed_this_season": 0
}
due lists every open round with something outstanding, naming the exact (match_id, team_id) pairs
absent. A round whose deadline has passed with values missing is counted, not listed, in
missed_this_season: nothing can be done about it now, and a list that never shrinks stops being
read.
certified is false until certification passes, and until then nothing is due. Reads are free and
count toward no limit — call it after every run.
Reminders
| When | What |
|---|---|
| Deadline − 12 h | Email to your contact address, listing what is still missing |
| Deadline − 3 h | The same, if anything is still missing |
Twelve hours rather than twenty-four, because a day before a Tuesday deadline is Monday night, when the round's last match may barely be over and every participant would look "missing".
A reminder is sent only when something is actually missing, so a participant whose job works never
receives one. Reminders are a safety net; status is how a well-built job checks itself.
Certification
Certification is a dry run against one completed round, put through every check a real delivery faces and storing nothing.
POST /api/tasks/game-value-team/values?dry_run=true
To pass, the dry run must cover every match of one completed round in a competition, both sides of
each, with nothing rejected. The receipt then carries "certified": true.
Certification runs against a round whose deadline has passed, so its entries come back
late. That is expected and correct. The only outcome that prevents certification isrejected.
Two consequences:
- Passing guarantees acceptance. A dry run runs the real checks, so what passes here passes for real. That is what makes it worth doing.
- Certifying declares what you are entering. The competitions your dry run covered are the ones you are scored on. Send one complete round per competition you intend to cover.
Certification is enrolment. You are scored from that moment — not from when you registered, and not from when you first delivered.
Coverage is all or nothing within a competition
You choose which competitions to enter. Inside one, you deliver every match of every round: a participant who priced only the fixtures it found easy would be scored on a different set of matches from everyone else, and the scores would not be comparable.
Joining mid-season
You may certify at any point in the season. Values for matches that kicked off before you
certified are refused as before enrolment — they could be priced knowing every result since, so
they would not be forecasts.
This is not a penalty for arriving late; it is the rule everyone is under. It does mean your delivered history starts the day you certify, and the first fixtures can only be scored once both sides have enough of it.
Refusals
The whole delivery
| Code | Cause |
|---|---|
401 | No valid API key |
403 | The participant has not entered this benchmark, or has not certified |
400 | task is not game_value_team |
409 | contract_version is not the one in force — the refusal names the version that is |
422 | The body is not shaped like the contract |
A single entry
Checked in this order; the first that applies is the reason given.
reason | Cause | Fix |
|---|---|---|
unknown match_id | No such fixture this season | Re-read the fixture list; never construct an identifier |
team did not play in this match | The team is not one of the two sides | Check your id mapping — usually a name match gone wrong |
match not played | The fixture's status is not played or awarded | Deliver after the match |
before enrolment | The match kicked off before you certified | Not accepted, by design |
value is not a number | Not a finite number | Send a number — not a string, null, boolean or NaN |
fixture described on one side only | Only one of the two teams is in this delivery | Send both sides together |
delivery limit reached for this round | A fourth stored delivery for that round | Batch your corrections; dry runs are free |
frozen | A different value after the deadline | The value in force stands |
Versioning
contract_version is checked on every delivery, and a mismatch is rejected rather than silently
accepted — a payload built against different rules is not a payload we can score.
What forces a new version: a change to the payload, the fixture-list fields, the receipt, the deadline rule, what the Arena derives, the burn-in, the horizons, or the statistic.
What does not: a new competition entering the fixture list, or a change to the published calendar. Those arrive through the fixture list itself, which is why your job reads it every run.
Every weekly snapshot records the contract version it was scored under, so a published row always says which rules produced it.
What to read next
- Onboarding — these calls in order, with a way to tell each one worked.
- Overview — what is measured, and why agreeing with the scoreline is not evidence.
- The team board — what a published participant looks like.