Start a casual or ranked match
const url = 'https://foxleague.ai/api/matches/play';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"match_type":"example","wallet_address":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}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_typeis 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>islineup,substitute benchorlineup 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>hfrom 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.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”The kind of match to start.
object
casual or ranked.
Required, but the session wallet is the one that plays.
Example generated
{ "match_type": "example", "wallet_address": "example"}Responses
Section titled “ Responses ”Success or a refusal: read success (the refusals are in the description).
object
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.
The answer. Present when success is true.
object
The opponent’s name; an AI team gets a generated name.
Your team’s name.
The new match’s id.
The pre-match card: both sides’ eleven starters, ratings, records and the head-to-head.
object
The away side.
object
Badge URI; absent when the team has none (always for an AI team).
Rating; the one recorded for the match when the card is for a match.
Formation, for example 4-3-3; empty when not known.
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.
How many teams the leaderboard holds, as total in GET /api/leaderboard. Present with leaderboard_place.
League: Bronze, Silver, Gold, Platinum or Diamond.
The lineup, in slot order; empty when not known.
A player on a pre-match card.
object
Country index; 0 for an AI player.
The player’s name.
Overall rating: the average of the seven attributes.
GK, DEF, MID or FWD; an AI player reads Goalkeeper, Defender, Midfielder or Forward.
Common, Uncommon, Rare, Epic or Legendary.
Shirt number.
Latest results whose match is public, newest first: W, D or L, at most 5. An AI team shows five generated results.
The player’s wins, draws and losses. An AI team shows a generated record.
object
Draws.
Losses.
Wins.
Average overall of the five best players, one decimal.
Team name.
Average overall of the listed players, one decimal.
The record between the two players.
object
Matches the away side won.
Draws.
Matches the home side won.
Matches between the two players whose result is public, developer tournaments left out; 0 against an AI team.
The home side.
object
Badge URI; absent when the team has none (always for an AI team).
Rating; the one recorded for the match when the card is for a match.
Formation, for example 4-3-3; empty when not known.
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.
How many teams the leaderboard holds, as total in GET /api/leaderboard. Present with leaderboard_place.
League: Bronze, Silver, Gold, Platinum or Diamond.
The lineup, in slot order; empty when not known.
A player on a pre-match card.
object
Country index; 0 for an AI player.
The player’s name.
Overall rating: the average of the seven attributes.
GK, DEF, MID or FWD; an AI player reads Goalkeeper, Defender, Midfielder or Forward.
Common, Uncommon, Rare, Epic or Legendary.
Shirt number.
Latest results whose match is public, newest first: W, D or L, at most 5. An AI team shows five generated results.
The player’s wins, draws and losses. An AI team shows a generated record.
object
Draws.
Losses.
Wins.
Average overall of the five best players, one decimal.
Team name.
Average overall of the listed players, one decimal.
The match’s status as it starts.
The match server’s game id.
The path of the match’s status socket, /ws/match/<match_id>.
Why the request was refused, in plain words. Present when success is false.
true when the request did what it asked. false is a refusal: read error.
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.
Example
Session expired or invalid