Skip to content

Start a casual or ranked match

POST
/api/matches/play
curl --request POST \
--url https://foxleague.ai/api/matches/play \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "match_type": "example", "wallet_address": "example" }'

Starts a casual or ranked match for the signed-in player. It answers with the new match id (when every match slot is busy it first waits for one to free, up to 30 seconds by default), the path of the socket that reports the match’s progress and the pre-match card of both sides; the match is then played and broadcast. Casual always meets an AI team at your lineup’s strength. Ranked meets a free real team near your rating when there is one, else an AI team; a ranked match against an AI team moves no rating. The same two teams meet in ranked at most once in 24 hours, either side at home: a team you met in ranked in the last 24 hours is not picked again (a match that failed or was abandoned is no meeting). A team whose stored starting eleven is not all match-fit right now (a starter under min_field_condition of /api/config or inside a transfer lock) is never picked either. Should a second ranked match of the same two teams still start inside those 24 hours (both picked at the same moment), it moves no rating; its result still counts in both teams’ records. The answer never carries the score.

Who: a signed-in player. The body’s wallet_address must be present but the session wallet is the one that plays.

Rate limit:

  • one start at a time per player;
  • a cooldown after your last match started (as home or away; a tournament match counts from its kickoff);
  • a longer cooldown between two ranked matches;
  • a daily cap on the casual and ranked matches you start, both modes together (read it with GET /api/matches/daily-allowance).

Refusals:

  • Invalid match_type. Use: casual, ranked: match_type is neither value.
  • Not registered: the session wallet has no account.
  • You need at least 18 unlisted players to enter a match. You currently have <n> (<short> short). Cancel <short> <listing|listings> on the Marketplace or buy <short> more <NFT|NFTs> to play again.: you hold fewer than 18 cards that are not listed for sale.
  • A match is already being processed. Please wait.: a match you started is still being set up or played.
  • Your squad is preparing for '<title>': next match in <m>m. Let your players rest and watch their stamina. You can play again after the tournament.: your team’s next tournament or challenge match kicks off within the hour.
  • Your squad's match in the '<title>' tournament has kicked off. You can play again once its matches are done.: your team’s tournament or challenge match has kicked off and its result is not out yet.
  • Your group in '<title>' is not finished yet. Let your players rest. You can play again once your group's results are out.: a groups and knockout tournament: your group still has a match whose broadcast has not ended and the group’s last kickoff is less than an hour away or past.
  • Round <n> of '<title>' is not finished yet. Let your players rest. You can play again once the round's results are out.: a Swiss tournament: your round still has a match whose broadcast has not ended and your kickoff in it is less than an hour away or past.
  • Already in match #<id>. Wait for it to finish before starting another.: you are in a ranked or casual match still being played or broadcast.
  • Your tournament match is on air. Wait for its broadcast to end before starting another.: a tournament or challenge match of yours is on air.
  • Match cooldown: wait <s> seconds: the cooldown after your last match is still running.
  • Ranked cooldown: your squad needs to rest. Wait <m>m (<s>s) before the next ranked match.: ranked only; the ranked cooldown is still running.
  • Daily match limit reached (<count>/<limit>). Try again tomorrow.: you reached the daily cap.
  • No team found: you have no team yet.
  • Invalid lineup: your saved lineup cannot be read; save your tactics again.
  • Invalid substitutes: your saved bench cannot be read; save your tactics again.
  • Lineup must have 11 players. Set your lineup first.: your lineup does not hold 11 players.
  • Only <n> of your 11 starters are in your squad. Set your lineup again.: some cards of your lineup are not in your squad any more.
  • Team must have exactly 7 substitutes (you have <n>). Visit the Tactics page to update your bench. The backend will auto-fill from your reserves.: your bench does not hold 7 players.
  • <n> player(s) in your team are listed for sale and are held in marketplace escrow, so they cannot play. Cancel the listing or remove them from your lineup/bench, then try again.: a starter or substitute is listed for sale.
  • Your <place> contains <n> player(s) not yet confirmed on-chain. If you just received these NFTs, wait ~60 seconds and retry. Otherwise refresh your team: they may have been transferred or fused.: a starter or substitute is not in your wallet on chain; <place> is lineup, substitute bench or lineup and substitute bench.
  • <name> has 0 condition and cannot play. Rest or use recovery item.: a starter has no condition left.
  • <name> was recently transferred (cooldown <time> remaining): a starter or substitute is inside its transfer lock; <time> reads <d>d <h>h from two days up, else <h>h <m>m.
  • Servers are busy with other matches right now. Please try again in a few seconds.: every match slot stayed busy for the whole wait or as many players as there are slots were already waiting for one.
  • The match server could not start your match. Please try again in a moment.: the match could not be started.
Media type application/json

The kind of match to start.

object
match_type
required

casual or ranked.

string
wallet_address
required

Required, but the session wallet is the one that plays.

string
Example generated
{
"match_type": "example",
"wallet_address": "example"
}

Success or a refusal: read success (the refusals are in the description).

Media type application/json
object
code

A machine-readable reason next to error, set only where the caller must act differently. tx_pending: the payment was not refused but could not be verified yet; keep the transaction hash and confirm again with the same hash, never pay twice.

string | null
data

The answer. Present when success is true.

object
away_team
required

The opponent’s name; an AI team gets a generated name.

string
home_team
required

Your team’s name.

string
match_id
required

The new match’s id.

integer format: int32
pre_match
required

The pre-match card: both sides’ eleven starters, ratings, records and the head-to-head.

object
away
required

The away side.

object
badge

Badge URI; absent when the team has none (always for an AI team).

string | null
elo_rating
required

Rating; the one recorded for the match when the card is for a match.

integer format: int32
formation
required

Formation, for example 4-3-3; empty when not known.

string
leaderboard_place

The team’s place on the leaderboard (1 is first), by the same ranking as GET /api/leaderboard. Absent for an AI side or when it cannot be read.

integer | null format: int64
leaderboard_total

How many teams the leaderboard holds, as total in GET /api/leaderboard. Present with leaderboard_place.

integer | null format: int64
league
required

League: Bronze, Silver, Gold, Platinum or Diamond.

string
players
required

The lineup, in slot order; empty when not known.

Array<object>

A player on a pre-match card.

object
country
required

Country index; 0 for an AI player.

integer format: int32
name
required

The player’s name.

string
overall
required

Overall rating: the average of the seven attributes.

integer format: int32
position
required

GK, DEF, MID or FWD; an AI player reads Goalkeeper, Defender, Midfielder or Forward.

string
rarity
required

Common, Uncommon, Rare, Epic or Legendary.

string
shirt_number
required

Shirt number.

integer format: int32
recent_form
required

Latest results whose match is public, newest first: W, D or L, at most 5. An AI team shows five generated results.

Array<string>
record
required

The player’s wins, draws and losses. An AI team shows a generated record.

object
drawn
required

Draws.

integer format: int32
lost
required

Losses.

integer format: int32
won
required

Wins.

integer format: int32
stars_overall
required

Average overall of the five best players, one decimal.

number format: double
team_name
required

Team name.

string
team_overall
required

Average overall of the listed players, one decimal.

number format: double
head_to_head
required

The record between the two players.

object
away_wins
required

Matches the away side won.

integer format: int32
draws
required

Draws.

integer format: int32
home_wins
required

Matches the home side won.

integer format: int32
last_result
One of:

The latest match between the two players.

object
away_score
required

Goals of this card’s away side.

integer format: int32
date
required

The match date, YYYY-MM-DD.

string
home_score
required

Goals of this card’s home side.

integer format: int32
total_matches
required

Matches between the two players whose result is public, developer tournaments left out; 0 against an AI team.

integer format: int32
home
required

The home side.

object
badge

Badge URI; absent when the team has none (always for an AI team).

string | null
elo_rating
required

Rating; the one recorded for the match when the card is for a match.

integer format: int32
formation
required

Formation, for example 4-3-3; empty when not known.

string
leaderboard_place

The team’s place on the leaderboard (1 is first), by the same ranking as GET /api/leaderboard. Absent for an AI side or when it cannot be read.

integer | null format: int64
leaderboard_total

How many teams the leaderboard holds, as total in GET /api/leaderboard. Present with leaderboard_place.

integer | null format: int64
league
required

League: Bronze, Silver, Gold, Platinum or Diamond.

string
players
required

The lineup, in slot order; empty when not known.

Array<object>

A player on a pre-match card.

object
country
required

Country index; 0 for an AI player.

integer format: int32
name
required

The player’s name.

string
overall
required

Overall rating: the average of the seven attributes.

integer format: int32
position
required

GK, DEF, MID or FWD; an AI player reads Goalkeeper, Defender, Midfielder or Forward.

string
rarity
required

Common, Uncommon, Rare, Epic or Legendary.

string
shirt_number
required

Shirt number.

integer format: int32
recent_form
required

Latest results whose match is public, newest first: W, D or L, at most 5. An AI team shows five generated results.

Array<string>
record
required

The player’s wins, draws and losses. An AI team shows a generated record.

object
drawn
required

Draws.

integer format: int32
lost
required

Losses.

integer format: int32
won
required

Wins.

integer format: int32
stars_overall
required

Average overall of the five best players, one decimal.

number format: double
team_name
required

Team name.

string
team_overall
required

Average overall of the listed players, one decimal.

number format: double
status
required

The match’s status as it starts.

string
tm_game_id
required

The match server’s game id.

integer format: int64
ws_url
required

The path of the match’s status socket, /ws/match/<match_id>.

string
error

Why the request was refused, in plain words. Present when success is false.

string | null
success
required

true when the request did what it asked. false is a refusal: read error.

boolean
Example
{
"data": {
"away_team": "Storm United",
"home_team": "Red Foxes",
"match_id": 5123,
"pre_match": {
"away": {
"elo_rating": 1240,
"formation": "4-4-2",
"league": "Silver",
"players": [
{
"country": 0,
"name": "AI Player #1",
"overall": 60,
"position": "Goalkeeper",
"rarity": "Common",
"shirt_number": 1
},
{
"country": 0,
"name": "AI Player #2",
"overall": 58,
"position": "Defender",
"rarity": "Common",
"shirt_number": 2
},
{
"country": 0,
"name": "AI Player #9",
"overall": 66,
"position": "Forward",
"rarity": "Common",
"shirt_number": 9
}
],
"recent_form": [
"W",
"D",
"W",
"L",
"W"
],
"record": {
"drawn": 5,
"lost": 5,
"won": 10
},
"stars_overall": 66,
"team_name": "Storm United",
"team_overall": 60.9
},
"head_to_head": {
"away_wins": 0,
"draws": 0,
"home_wins": 0,
"last_result": null,
"total_matches": 0
},
"home": {
"badge": "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi/badge.png",
"elo_rating": 1254,
"formation": "4-3-3",
"league": "Silver",
"players": [
{
"country": 12,
"name": "Vulpes Keeper",
"overall": 63,
"position": "GK",
"rarity": "Uncommon",
"shirt_number": 1
},
{
"country": 31,
"name": "Ion Lupu",
"overall": 59,
"position": "DEF",
"rarity": "Common",
"shirt_number": 4
},
{
"country": 7,
"name": "Marco Vela",
"overall": 71,
"position": "FWD",
"rarity": "Rare",
"shirt_number": 9
}
],
"recent_form": [
"W",
"L",
"W",
"D",
"W"
],
"record": {
"drawn": 4,
"lost": 9,
"won": 12
},
"stars_overall": 68.2,
"team_name": "Red Foxes",
"team_overall": 61.4
}
},
"status": "simulating",
"tm_game_id": 90412,
"ws_url": "/ws/match/5123"
},
"success": true
}

No valid session. The body is plain text: Missing Authorization header, Invalid Authorization format, Invalid session token or Session expired or invalid. Sign in again.

Media type text/plain
string
Example
Session expired or invalid