Authorize a paid 1v1 challenge for one signed transaction
const url = 'https://foxleague.ai/api/tournaments/challenges/authorize';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"challenge_deadline_minutes":1,"deadline":"2026-04-15T12:00:00Z","description":"example","developer_mode":true,"entry_fee_foxsy":1,"format":"example","has_third_place_match":true,"home_and_away":true,"max_teams":1,"min_teams":1,"organizer_only":true,"ovr_max":1,"ovr_min":1,"round_interval_minutes":1,"snapshot_at":"2026-04-15T12:00:00Z","sponsor_prize_foxsy":1,"starts_at":"2026-04-15T12:00:00Z","target_user_id":1,"tier":"example","title":"example","whitelist_user_ids":[1]}'};
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/tournaments/challenges/authorize \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "challenge_deadline_minutes": 1, "deadline": "2026-04-15T12:00:00Z", "description": "example", "developer_mode": true, "entry_fee_foxsy": 1, "format": "example", "has_third_place_match": true, "home_and_away": true, "max_teams": 1, "min_teams": 1, "organizer_only": true, "ovr_max": 1, "ovr_min": 1, "round_interval_minutes": 1, "snapshot_at": "2026-04-15T12:00:00Z", "sponsor_prize_foxsy": 1, "starts_at": "2026-04-15T12:00:00Z", "target_user_id": 1, "tier": "example", "title": "example", "whitelist_user_ids": [ 1 ] }'Asks the server to authorize a paid 1v1 challenge so that you create it and pay its entry fee in ONE transaction of yours, createAndJoin, instead of a server create followed by your joinTournament. Send the same body as POST /api/tournaments for a Silver or Gold challenge with max_teams: 2. The server runs every check the normal create runs, writes the challenge in a hidden state that only you can see and answers the signed terms: the exact fee, the allowed wallets in order, the expiry in chain time and the server’s signature. Build the transaction from them exactly as answered and pay; the contract refuses any other amount, wallet, order or key. Then call POST /api/tournaments/challenges/{id}/confirm.
Nothing is created on chain by this call. Calling it again with the same body before the expiry answers the same authorization with the seconds left. A different body while one of yours can still be paid is refused until its window closes. When a previous authorization of yours turns out paid, the answer has state: "open" and its tournament_id: open that challenge, there is nothing to sign.
Offered only while GET /api/config answers tournament_defaults.challenge_create_and_join: true.
Who: a signed-in player with a team.
Refusals:
The one-signature challenge flow is not available right now. Create the challenge the usual way.: the flow is switched off or the contract does not offer it yet.Chain is temporarily unavailable. Please retry shortly.: the server cannot reach the chain right now.The chain clock could not be read. Please retry in a moment.: the expiry is chain time and the chain time could not be read.This wallet cannot create a challenge.: an operator or contract wallet.The one-signature flow creates paid 1v1 challenges only (Silver or Gold, two seats). Create other tournaments the usual way.: the body is not a paid 1v1 challenge.Developer challenges are created the usual way.: the body hasdeveloper_mode: true. A developer challenge is created withPOST /api/tournamentsand joined withPOST /api/tournaments/{id}/join, which runs the developer checks.You already started a challenge for <fee> FOXSY against <team>. Finish it in the wallet or wait <N>s to start a different one.: a previous authorization of yours can still be paid.You already have an active 1v1. Finish it or cancel it first.: you are in another live challenge (an authorized one counts until its window closes).- Every refusal of
POST /api/tournamentsfor a 1v1 challenge, in the same words. Signing service unavailable. Please retry.: the server could not sign; nothing was written.
Authorizations
Section titled “Authorizations ”Request Body required
Section titled “Request Body required ”The settings of a new tournament or 1v1 challenge.
object
1v1 challenge only: minutes the challenge stays open, 30, 60, 120 or 240 (default 60). Kickoff is five minutes after it closes; when an opponent accepts, registration closes at once and the match is scheduled shortly after.
Registration closes at this time (RFC 3339); it must be in the future. Required but ignored for a 1v1 challenge.
Optional text, at most 1000 characters.
Only teams with a verified developer binary may join. Available on every format except Champions FoxLeague.
Entry fee in whole FOXSY. 0 or absent for Bronze. Silver and Gold need a fee inside the tier’s range; the refusal names the range.
knockout, groups_knockout, round_robin, swiss or league_knockout (Champions FoxLeague: created by an admin only, while the admin has switched the format on). A 1v1 challenge is knockout.
Adds a match for third place between the losing semifinalists. Default false.
round_robin only: every pair of teams meets twice. Ignored for other formats.
Seats. knockout: 2, 4, 8, 16 or 32 (2 makes a 1v1 challenge). groups_knockout: 8, 16, 32 or 64. round_robin: 3 to 20. swiss: 3 to 32. league_knockout: 36.
Teams needed to start; with fewer at the deadline the tournament is cancelled. At least 2 for knockout, 8 for groups_knockout, 36 for league_knockout and 3 for round_robin and swiss; at most max_teams. Equal to max_teams for knockout, groups_knockout and league_knockout.
Standard tournament only: you run the tournament without a seat and cannot join it later.
Optional highest best XI OVR a joining team may have: a whole number from 1 to 100, not below ovr_min.
Optional lowest best XI OVR a joining team may have: a whole number from 1 to 100, not above ovr_max. Not for Champions FoxLeague or a developer tournament. If you take a seat, your own best XI OVR must be inside the range.
Minutes between one match’s kickoff and the next: the tournament plays its matches one after another. Default 30; a lower value counts as 30; at most 1440.
Champions FoxLeague only (required there): when the leaderboard is frozen and the first invitation wave goes out. In the future and at least 4 days (an admin setting) before deadline. Refused for every other format.
Champions FoxLeague only, optional: a prize from FoxLeague in whole FOXSY, at least 1, split equally among the 8 quarter-finalists by the tournaments contract. Only on a Silver or Gold tournament and only when your wallet is an approved sponsor on the contract. Your wallet deposits it after the create, before the snapshot.
Kickoff of the first match (RFC 3339): more than one hour from now, more than five minutes after deadline and at most 90 days ahead. Required but ignored for a 1v1 challenge.
1v1 challenge only: the user id of the only opponent who may accept. Not your own id.
bronze (free), silver or gold (paid in FOXSY). A 1v1 challenge is Silver or Gold; Bronze only for a developer challenge.
Name of the tournament, 1 to 100 characters, not blank.
Standard tournament only, never Champions FoxLeague (its invitations come from the leaderboard): user ids of the teams you invite. A non-empty list makes the tournament private. Your own id and repeated ids are dropped; at most max_teams ids.
Example generated
{ "challenge_deadline_minutes": 1, "deadline": "2026-04-15T12:00:00Z", "description": "example", "developer_mode": true, "entry_fee_foxsy": 1, "format": "example", "has_third_place_match": true, "home_and_away": true, "max_teams": 1, "min_teams": 1, "organizer_only": true, "ovr_max": 1, "ovr_min": 1, "round_interval_minutes": 1, "snapshot_at": "2026-04-15T12:00:00Z", "sponsor_prize_foxsy": 1, "starts_at": "2026-04-15T12:00:00Z", "target_user_id": 1, "tier": "example", "title": "example", "whitelist_user_ids": [ 1 ]}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 allowed wallets, in the exact order to send them: you first, then the challenged player for a targeted challenge.
The exact amount the transaction must carry, in FOXSY atomic units, as a decimal string.
The entry fee in whole FOXSY.
Seconds left on expiry at the time of this answer, by chain time. In a pending refusal: the seconds to wait before a different challenge can start.
Chain time in seconds up to and including which the contract accepts the signature.
The FOXSY token to pay with.
The gas limit to send the transaction with.
The seat cap to send: 2.
The tournaments contract to send the transaction to.
The server’s 64-byte signature of the terms, as hexadecimal.
authorized: sign and pay with the fields below. pending (inside a refusal with code challenge_authorization_pending): a previous authorization of yours can still land; tournament_id is that challenge and expires_in_seconds the seconds to wait before a different one can start. Any other value is the status of a previous challenge of yours that turned out paid (open): nothing to sign, go to tournament_id.
The challenge id: the first argument of the transaction and the id to confirm.
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
{ "success": true, "data": { "state": "authorized", "tournament_id": 1908, "entry_fee_atomic": "1000000000000000000000", "entry_fee_foxsy": 1000, "max_entries": 2, "allowed_wallets": [ "erd1creator000000000000000000000000000000000000000000000000000", "erd1target0000000000000000000000000000000000000000000000000000" ], "expiry": 1792300600, "signature_hex": "abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab", "sc_address": "erd1qqqqqqqqqqqqqpgqfw3nadv3z7hj6c2x7alndtw03y5ang43utesz8nmg9", "foxsy_token_id": "FOXSY-abc123", "gas_limit": 18480000, "expires_in_seconds": 600 }}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.
The answer of a request that returns no data. POST /api/tournaments/{id}/delete answers its refusals with it: success is false and error says why.
object
A machine-readable reason next to error, set only where the caller must act differently.
Always null on success: the route returns no data.
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 generated
{ "code": "example", "data": "example", "error": "example", "success": true}