Skip to content

Confirm a paid 1v1 challenge from the chain

POST
/api/tournaments/challenges/{id}/confirm
curl --request POST \
--url https://foxleague.ai/api/tournaments/challenges/1/confirm \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "tx_hash": "example" }'

Opens your authorized challenge once your createAndJoin payment is on chain. Send tx_hash when you have it; the seat is proven by the chain state, never by the hash: the contract must hold the tournament open with your entry at the fee. Without a hash the call is the question “did my payment land?” and answers one of three things in state: open (paid: the challenge is open with your seat), not_paid (the chain holds no payment of yours; pool_nonces and account_nonce tell you whether a transaction of yours is still pending, so you never sign twice) or unreadable (the chain could not be read; nothing was charged twice, retry in a moment). A challenge that could not open answers cancelled with the reason; any fee that landed is refunded.

Safe to repeat: an open challenge answers its seat again and a hash sent later for a seat recorded without one is checked and recorded.

Who: a signed-in player, the creator of the challenge.

Refusals:

  • Tournament not found: no challenge has this id.
  • Only the creator of this challenge can confirm it.: another wallet asked (status 403).
  • Chain is temporarily unavailable. Please retry shortly.: the server has no chain connection.
  • Could not open your challenge right now. Your payment is on chain; please retry in a moment.: the server could not write the seat; the payment is safe and the server also retries by itself.
id
required
integer format: int64

The challenge id answered by the authorize call.

Media type application/json

The hash of your createAndJoin transaction, when you have it.

object
tx_hash

The hash of your createAndJoin transaction. Leave it out to ask whether a payment of yours landed.

string | null
Example generated
{
"tx_hash": "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
account_nonce

not_paid only: your executed account nonce. Sign again with the recorded nonce only while this has not moved past it.

integer | null format: int64
entry_tx_hash

The entry transaction recorded for your seat, when one was named.

string | null
message
required

What happened, in plain words.

string
pool_nonces

not_paid only: the nonces of your pending transactions one observer sees in the pool. While your recorded nonce is here, wait instead of signing again.

Array<integer> | null
seated
required

Whether you hold the challenge’s first seat. true is the one fact that means paid.

boolean
state
required

open: paid, your seat is recorded. cancelled: the challenge could not open or was closed unpaid; message says which and any fee that landed is refunded. not_paid: the chain holds no payment of yours yet. unreadable: the chain could not be read; retry in a moment. A challenge past its opening answers its status (active, completed).

string
tournament
One of:

A tournament or 1v1 challenge.

object
away_penalty

Completed 1v1 challenge rows decided on penalties: shootout goals of the away side. Absent otherwise.

integer | null format: int32
away_score

Completed 1v1 challenge rows of the tournament lists: goals of the away side. Absent otherwise.

integer | null format: int32
blocking_window_end

End of that reserved window (RFC 3339): one hour after the tournament’s booked play time, about 3 minutes per match without the time between games; null when there is none. This is not when the tournament ends: it can keep playing after its window for hours or days.

string | null format: date-time
blocking_window_start

Start of the time window a standard tournament reserves on the schedule, one hour before kickoff (RFC 3339). No other tournament may reserve an overlapping window. null for 1v1 challenges and developer tournaments.

string | null format: date-time
cancelled_at

When the tournament was cancelled (RFC 3339); null when it was not.

string | null format: date-time
challenge_deadline_minutes

1v1 challenge: the minutes it was opened for. null for a standard tournament.

integer | null format: int32
completed_at

When the tournament completed (RFC 3339); null before.

string | null format: date-time
created_at
required

When the tournament was created (RFC 3339).

string format: date-time
creator_badge

1v1 challenge rows of the tournament lists: the creator’s team badge. Absent otherwise.

string | null
creator_ovr

1v1 challenge rows still to be played (not completed nor cancelled, not a developer one): the creator’s team OVR, one decimal, its best XI OVR (the average OVR of the strongest XI it can field from the cards it owns now (for every formation in Tactics the best card of each position for its slots, each card in its own position, the highest average kept; cards listed for sale count)). Absent otherwise and when no card it owns fits a slot.

number | null format: double
creator_ovr_formation

The formation the creator’s best XI is built in, such as 4-3-3. Absent with creator_ovr.

string | null
creator_team_name

1v1 challenge rows of the tournament lists: the creator’s team name. Absent otherwise.

string | null
creator_user_id
required

User id of the creator (the organizer).

integer format: int32
deadline
required

Registration closes at this time (RFC 3339).

string format: date-time
decided_by

Completed 1v1 challenge rows of the tournament lists: regular, extra_time or penalties. Absent otherwise.

string | null
description

Text written by the creator; null when none.

string | null
developer_mode

true when only teams with a verified developer binary may join.

boolean | null
distribute_tx_hash

Completed 1v1 challenge rows of the tournament lists: hash of the prize payout transaction. Absent otherwise.

string | null
entry_fee_foxsy
required

Entry fee in whole FOXSY; 0 for Bronze.

integer format: int64
featured
required

true for a tournament the game features: it is listed first and enters the hall of fame when it completes.

boolean
format
required

knockout, groups_knockout, round_robin, swiss or league_knockout (Champions FoxLeague: 36 teams in one league table drawn from four pots, then two-legged knockout ties up to a single-match final).

string
has_third_place_match
required

true when a match for third place is played. A Champions FoxLeague tournament always plays one, before its final.

boolean
home_and_away

true for a round robin where every pair of teams meets twice.

boolean | null
home_penalty

Completed 1v1 challenge rows decided on penalties: shootout goals of the home side. Absent otherwise.

integer | null format: int32
home_score

Completed 1v1 challenge rows of the tournament lists: goals of the home side. Absent otherwise.

integer | null format: int32
home_user_id

Completed 1v1 challenge rows of GET /api/tournaments: user id of the home side, to match the score to the teams (the creator is not always at home). Absent otherwise.

integer | null format: int32
id
required

The tournament id.

integer format: int64
is_private

true when only invited teams may join.

boolean
max_teams
required

Seats. 2 for a 1v1 challenge.

integer format: int32
min_teams
required

Teams needed to start; with fewer at the deadline the tournament is cancelled.

integer format: int32
opponent_badge

1v1 challenge rows of the tournament lists: the opponent’s team badge once an opponent holds the second seat. Absent otherwise.

string | null
opponent_ovr

The opponent’s team OVR, read like creator_ovr, once an opponent holds the second seat. Absent otherwise.

number | null format: double
opponent_ovr_formation

The formation the opponent’s best XI is built in. Absent with opponent_ovr.

string | null
opponent_team_name

1v1 challenge rows of the tournament lists: the opponent’s team name once an opponent holds the second seat. Absent otherwise.

string | null
organizer_only

Tournament detail only: true when the creator runs the tournament without a seat. Absent elsewhere.

boolean | null
ovr_max

The highest best XI OVR a team may have to join, a whole number from 1 to 100. Absent when there is none. While registration is open its creator or an admin can raise it or remove it; it never drops.

integer | null format: int32
ovr_min

The lowest best XI OVR a team may have to join, a whole number from 1 to 100. Absent when there is none. The best XI OVR is the average OVR of the strongest XI a manager can field from the cards they own; it is checked once, when a team joins. While registration is open its creator or an admin can lower it or remove it (POST /api/tournaments/{id}/ovr-range); it never rises.

integer | null format: int32
participant_count

Seats taken. Filled by GET /api/tournaments; null in other answers.

integer | null format: int64
round_interval_minutes
required

Minutes between one match’s kickoff and the next.

integer format: int32
runner_up_user_id

User id of the runner-up, set when the tournament completes; null before.

integer | null format: int32
sc_tournament_id

Id of the tournament on chain (equal to id) for a paid tournament once it is created there; null for a free tournament.

integer | null format: int64
started_at

When registration closed and the draw was made (RFC 3339); null before.

string | null format: date-time
starts_at
required

Kickoff of the first match (RFC 3339).

string format: date-time
status
required

open: registration is open. pending_sc: a paid tournament is being created on chain and cannot be joined yet. active: registration closed and the tournament is under way. completed: finished, podium set. cancelled: too few teams at the deadline or cancelled by its creator.

string
target_user_id

Targeted 1v1 challenge: user id of the only opponent who may accept. Given to the creator and the target only; null for everyone else and for any other tournament.

integer | null format: int32
targeted

true for a 1v1 challenge locked to one opponent. Absent otherwise.

boolean
targeted_to_you

true when you are the opponent a targeted 1v1 challenge is locked to. Absent otherwise.

boolean
third_place_user_id

User id of the third-placed team, set when the tournament completes; null before and when no third place is decided.

integer | null format: int32
tier
required

bronze (free), silver or gold (paid in FOXSY).

string
title
required

Name of the tournament.

string
winner_user_id

User id of the winner, set when the tournament completes; null before.

integer | null format: int32
tournament_id
required

The challenge id.

integer format: int64
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
{
"success": true,
"data": {
"tournament_id": 1908,
"state": "not_paid",
"seated": false,
"entry_tx_hash": null,
"tournament": null,
"message": "The chain holds no payment of yours for this challenge yet.",
"pool_nonces": [
41
],
"account_nonce": 40
}
}

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 application/json

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
code

A machine-readable reason next to error, set only where the caller must act differently.

string
data

Always null on success: the route returns no data.

error

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

string
success
required

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

boolean
Example generated
{
"code": "example",
"data": "example",
"error": "example",
"success": true
}

See 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
account_nonce

not_paid only: your executed account nonce. Sign again with the recorded nonce only while this has not moved past it.

integer | null format: int64
entry_tx_hash

The entry transaction recorded for your seat, when one was named.

string | null
message
required

What happened, in plain words.

string
pool_nonces

not_paid only: the nonces of your pending transactions one observer sees in the pool. While your recorded nonce is here, wait instead of signing again.

Array<integer> | null
seated
required

Whether you hold the challenge’s first seat. true is the one fact that means paid.

boolean
state
required

open: paid, your seat is recorded. cancelled: the challenge could not open or was closed unpaid; message says which and any fee that landed is refunded. not_paid: the chain holds no payment of yours yet. unreadable: the chain could not be read; retry in a moment. A challenge past its opening answers its status (active, completed).

string
tournament
One of:

A tournament or 1v1 challenge.

object
away_penalty

Completed 1v1 challenge rows decided on penalties: shootout goals of the away side. Absent otherwise.

integer | null format: int32
away_score

Completed 1v1 challenge rows of the tournament lists: goals of the away side. Absent otherwise.

integer | null format: int32
blocking_window_end

End of that reserved window (RFC 3339): one hour after the tournament’s booked play time, about 3 minutes per match without the time between games; null when there is none. This is not when the tournament ends: it can keep playing after its window for hours or days.

string | null format: date-time
blocking_window_start

Start of the time window a standard tournament reserves on the schedule, one hour before kickoff (RFC 3339). No other tournament may reserve an overlapping window. null for 1v1 challenges and developer tournaments.

string | null format: date-time
cancelled_at

When the tournament was cancelled (RFC 3339); null when it was not.

string | null format: date-time
challenge_deadline_minutes

1v1 challenge: the minutes it was opened for. null for a standard tournament.

integer | null format: int32
completed_at

When the tournament completed (RFC 3339); null before.

string | null format: date-time
created_at
required

When the tournament was created (RFC 3339).

string format: date-time
creator_badge

1v1 challenge rows of the tournament lists: the creator’s team badge. Absent otherwise.

string | null
creator_ovr

1v1 challenge rows still to be played (not completed nor cancelled, not a developer one): the creator’s team OVR, one decimal, its best XI OVR (the average OVR of the strongest XI it can field from the cards it owns now (for every formation in Tactics the best card of each position for its slots, each card in its own position, the highest average kept; cards listed for sale count)). Absent otherwise and when no card it owns fits a slot.

number | null format: double
creator_ovr_formation

The formation the creator’s best XI is built in, such as 4-3-3. Absent with creator_ovr.

string | null
creator_team_name

1v1 challenge rows of the tournament lists: the creator’s team name. Absent otherwise.

string | null
creator_user_id
required

User id of the creator (the organizer).

integer format: int32
deadline
required

Registration closes at this time (RFC 3339).

string format: date-time
decided_by

Completed 1v1 challenge rows of the tournament lists: regular, extra_time or penalties. Absent otherwise.

string | null
description

Text written by the creator; null when none.

string | null
developer_mode

true when only teams with a verified developer binary may join.

boolean | null
distribute_tx_hash

Completed 1v1 challenge rows of the tournament lists: hash of the prize payout transaction. Absent otherwise.

string | null
entry_fee_foxsy
required

Entry fee in whole FOXSY; 0 for Bronze.

integer format: int64
featured
required

true for a tournament the game features: it is listed first and enters the hall of fame when it completes.

boolean
format
required

knockout, groups_knockout, round_robin, swiss or league_knockout (Champions FoxLeague: 36 teams in one league table drawn from four pots, then two-legged knockout ties up to a single-match final).

string
has_third_place_match
required

true when a match for third place is played. A Champions FoxLeague tournament always plays one, before its final.

boolean
home_and_away

true for a round robin where every pair of teams meets twice.

boolean | null
home_penalty

Completed 1v1 challenge rows decided on penalties: shootout goals of the home side. Absent otherwise.

integer | null format: int32
home_score

Completed 1v1 challenge rows of the tournament lists: goals of the home side. Absent otherwise.

integer | null format: int32
home_user_id

Completed 1v1 challenge rows of GET /api/tournaments: user id of the home side, to match the score to the teams (the creator is not always at home). Absent otherwise.

integer | null format: int32
id
required

The tournament id.

integer format: int64
is_private

true when only invited teams may join.

boolean
max_teams
required

Seats. 2 for a 1v1 challenge.

integer format: int32
min_teams
required

Teams needed to start; with fewer at the deadline the tournament is cancelled.

integer format: int32
opponent_badge

1v1 challenge rows of the tournament lists: the opponent’s team badge once an opponent holds the second seat. Absent otherwise.

string | null
opponent_ovr

The opponent’s team OVR, read like creator_ovr, once an opponent holds the second seat. Absent otherwise.

number | null format: double
opponent_ovr_formation

The formation the opponent’s best XI is built in. Absent with opponent_ovr.

string | null
opponent_team_name

1v1 challenge rows of the tournament lists: the opponent’s team name once an opponent holds the second seat. Absent otherwise.

string | null
organizer_only

Tournament detail only: true when the creator runs the tournament without a seat. Absent elsewhere.

boolean | null
ovr_max

The highest best XI OVR a team may have to join, a whole number from 1 to 100. Absent when there is none. While registration is open its creator or an admin can raise it or remove it; it never drops.

integer | null format: int32
ovr_min

The lowest best XI OVR a team may have to join, a whole number from 1 to 100. Absent when there is none. The best XI OVR is the average OVR of the strongest XI a manager can field from the cards they own; it is checked once, when a team joins. While registration is open its creator or an admin can lower it or remove it (POST /api/tournaments/{id}/ovr-range); it never rises.

integer | null format: int32
participant_count

Seats taken. Filled by GET /api/tournaments; null in other answers.

integer | null format: int64
round_interval_minutes
required

Minutes between one match’s kickoff and the next.

integer format: int32
runner_up_user_id

User id of the runner-up, set when the tournament completes; null before.

integer | null format: int32
sc_tournament_id

Id of the tournament on chain (equal to id) for a paid tournament once it is created there; null for a free tournament.

integer | null format: int64
started_at

When registration closed and the draw was made (RFC 3339); null before.

string | null format: date-time
starts_at
required

Kickoff of the first match (RFC 3339).

string format: date-time
status
required

open: registration is open. pending_sc: a paid tournament is being created on chain and cannot be joined yet. active: registration closed and the tournament is under way. completed: finished, podium set. cancelled: too few teams at the deadline or cancelled by its creator.

string
target_user_id

Targeted 1v1 challenge: user id of the only opponent who may accept. Given to the creator and the target only; null for everyone else and for any other tournament.

integer | null format: int32
targeted

true for a 1v1 challenge locked to one opponent. Absent otherwise.

boolean
targeted_to_you

true when you are the opponent a targeted 1v1 challenge is locked to. Absent otherwise.

boolean
third_place_user_id

User id of the third-placed team, set when the tournament completes; null before and when no third place is decided.

integer | null format: int32
tier
required

bronze (free), silver or gold (paid in FOXSY).

string
title
required

Name of the tournament.

string
winner_user_id

User id of the winner, set when the tournament completes; null before.

integer | null format: int32
tournament_id
required

The challenge id.

integer format: int64
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 generated
{
"code": "example",
"data": {
"account_nonce": 1,
"entry_tx_hash": "example",
"message": "example",
"pool_nonces": [
1
],
"seated": true,
"state": "example",
"tournament": {
"away_penalty": 1,
"away_score": 1,
"blocking_window_end": "2026-04-15T12:00:00Z",
"blocking_window_start": "2026-04-15T12:00:00Z",
"cancelled_at": "2026-04-15T12:00:00Z",
"challenge_deadline_minutes": 1,
"completed_at": "2026-04-15T12:00:00Z",
"created_at": "2026-04-15T12:00:00Z",
"creator_badge": "example",
"creator_ovr": 1,
"creator_ovr_formation": "example",
"creator_team_name": "example",
"creator_user_id": 1,
"deadline": "2026-04-15T12:00:00Z",
"decided_by": "example",
"description": "example",
"developer_mode": true,
"distribute_tx_hash": "example",
"entry_fee_foxsy": 1,
"featured": true,
"format": "example",
"has_third_place_match": true,
"home_and_away": true,
"home_penalty": 1,
"home_score": 1,
"home_user_id": 1,
"id": 1,
"is_private": true,
"max_teams": 1,
"min_teams": 1,
"opponent_badge": "example",
"opponent_ovr": 1,
"opponent_ovr_formation": "example",
"opponent_team_name": "example",
"organizer_only": true,
"ovr_max": 1,
"ovr_min": 1,
"participant_count": 1,
"round_interval_minutes": 1,
"runner_up_user_id": 1,
"sc_tournament_id": 1,
"started_at": "2026-04-15T12:00:00Z",
"starts_at": "2026-04-15T12:00:00Z",
"status": "example",
"target_user_id": 1,
"targeted": true,
"targeted_to_you": true,
"third_place_user_id": 1,
"tier": "example",
"title": "example",
"winner_user_id": 1
},
"tournament_id": 1
},
"error": "example",
"success": true
}