Reporting Match Results
Ratings only change when you tell GameFlow how a match ended. After each finished match, your backend reports the teams and who won; GameFlow scores it with the model, updates every player's μ and σ, and stores the result. Between matches you can read a player's current rating to show a rank.
Both calls go to the GameFlow API base URL and send your game's key in the
X-Api-Key header. Your backend holds the key; the client never sees it. GameFlow
resolves which model to use from the live matchmaker for the game and mode, so you
never send a model id.
Reporting works only when a live matchmaker for that game mode has a Skill Rating Model node. Without one, the report is rejected because no model is configured for the mode. See Using a Model with a Matchmaker.
Report a result
Send the teams and a parallel ranks array. Lower rank is better, so the winning
team is rank 1. Give teams equal ranks to express a draw.
const res = await fetch(`${GAMEFLOW_API_URL}/skill-rating/matches:report`, {
method: "POST",
headers: { "X-Api-Key": apiKey, "Content-Type": "application/json" },
body: JSON.stringify({
game_id: gameId,
game_mode: "default", // resolves the model, like the ticket's mode
match_id: gameflowMatchId, // GameFlow's match id, links the result to analytics
external_match_id: yourOwnId, // your id for the match, for idempotency/audit
teams: [
{ players: [{ external_player_id: "p1", display_name: "Ana" }] },
{ players: [{ external_player_id: "p2", display_name: "Beto" }] },
],
ranks: [1, 2], // team 0 won, team 1 lost
}),
});
const { ratings } = await res.json();
| Field | Description |
|---|---|
game_id | Your GameFlow game id. |
game_mode | The mode that was played. Resolves which model scores the match. |
match_id | Optional. GameFlow's own match id, the one your server received in GAMEFLOW_PAYLOAD. Links this result to the match's analytics. Ratings are recorded identically without it; only the link is lost. See Match Analytics. |
external_match_id | Your own match identifier. Stored with the result. |
teams | The teams, each a list of players by external_player_id (and optional display_name). |
ranks | One entry per team, in the same order. Lower is better; equal ranks mean a draw. |
The response returns the updated rating for every player:
| Field | Description |
|---|---|
skill_model_id, season_id | What the result was recorded against. |
match_id | The rating record's own id. This is not the match_id you send in the request, which is GameFlow's match id. |
ratings[] | Per player: mu_before / sigma_before, mu_after / sigma_after, and ordinal_after (the conservative μ − 3σ). |
A player with no prior rating starts from the model's initial μ / σ, so their first report both creates and updates their rating.
Read a player's rating
To show a rank before or after a match, resolve the player's current rating for the game and mode:
const res = await fetch(`${GAMEFLOW_API_URL}/skill-rating/player-rating:resolve`, {
method: "POST",
headers: { "X-Api-Key": apiKey, "Content-Type": "application/json" },
body: JSON.stringify({
game_id: gameId,
game_mode: "default",
external_player_id: "p1",
}),
});
const data = await res.json();
if (data.found) {
const { mu, sigma, ordinal } = data.rating; // ordinal = μ − 3σ, for display
}
found is false when no model is configured for that game and mode, which is a
valid FIFO setup. Treat it as "no rank to show" and hide the widget, rather than an
error.
Full example
The Colyseus multiplayer example
implements this exact flow: its game server reports the finished 2v2 to
matches:report with the winning team ranked 1, and the client shows each player's
resolved rank. The game side stays model-agnostic and sends only the game id and
mode.
Related
- Using a Model with a Matchmaker: the setup that makes reporting resolvable.
- Configuring a Model: the behaviour settings that shape how much each report moves a rating.
- Implementing In Your Game: the ticket API that gets players into the match in the first place.
- Match Analytics: what
match_idlinks this result to, and the two lifecycle calls that go with it.