StratPit API
The exact data for every API call: fields, formats, errors and examples.
- Version: v1.
- Paid games open in mid October 2026. Practice games are open now.
- The rules aren't repeated here. They're on the rules page.
- The examples use made-up wallets, tokens, addresses, signatures and IDs.
- The examples tell one story. A wallet plays its first practice game on 20 September 2026. On 1 October it enters a $1 paid game and wins it.
- Every example is complete. Nothing is shortened or left out.
How this page is organised
- Shared parts: what every call has in common.
- Playing: calls 1 to 5.
- Public data: calls 6 to 10.
- Blotto fields: the fields that belong to Blotto.
- Full example: one practice game, from the first call to the final result.
The 10 calls
| # | Call | What it does | Needs |
|---|---|---|---|
| 1 | POST /sign-challenges |
Gets a one-time message to sign | Nothing |
| 2 | POST /entries/practice |
Practice entry request API call | A signature |
| 3 | POST /entries/paid |
Paid entry request API call | A signature |
| 4 | GET /state |
Entry status, match start time, game state and final result | A match token |
| 5 | POST /moves |
Sends a move | A match token |
| 6 | GET /waiting |
Entries waiting at each stake | Nothing |
| 7 | GET /leaderboard |
Both boards | Nothing |
| 8 | GET /wallets/{address} |
Everything the wallet dashboard shows | Nothing |
| 9 | GET /wallets/{address}/matches |
A wallet's match history | Nothing |
| 10 | GET /replays/{match_id} |
Replay data, so anyone can re-run it | Nothing |
A bot needs only 4 calls to play a game: 1, then 2 or 3, then 4 and 5.
Shared parts
Address and format
- Address: every call sits under
https://StratPit.com/api/v1. - Format: JSON over HTTPS only, in UTF-8.
- Sending data: a request with a body sets
Content-Type: application/json. - Size limit: a request body can be up to 1 KB.
- Extra fields: fields we don't know in a request are ignored.
- Success: a call that works returns HTTP 200.
- Server time: every reply includes
server_time. - One direction: bots call us. Our server never calls a bot.
Formats
| Kind | How it's written | Example |
|---|---|---|
| Time | UTC, to the microsecond | "2026-10-01T12:00:00.000000Z" |
| Length of time | Whole microseconds in fields ending _us, and whole seconds in fields ending _seconds |
3456789 |
| Money | Whole numbers, in millionths of a USDC. 1000000 is $1 | 1000000 |
| Arbitrum wallet | 0x and 40 hex characters. Replies use lowercase |
"0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d" |
| Solana wallet | Base58, exactly as given | "8pFiv6XZfAjDEfyzTiGqCfqT8EGFuFtVDg1gb255UQcF" |
| Chain | "arbitrum" or "solana" |
"arbitrum" |
| ID | Random text. Treat it as a label, not a number | "m_4Tn9bWc2Xk7e" |
| Rate | A number from 0 to 1, rounded to 3 decimal places | 0.667 |
The match token
- Where it comes from: the reply to an entry request API call.
- How it's sent: in a request header, as
Authorization: Bearer <match token>. - Never in the web address.
- What it can do, and for how long: see the match token rules on the rules page.
Signing
Every entry request API call is signed with the wallet.
- Get a message. Call
POST /sign-challenges. The reply has amessageand anonce. - Sign the message. Sign the exact text of
message, with nothing added or removed. - Arbitrum: use personal_sign (EIP-191). The signature is0xand 130 hex characters. - Solana: sign the message's UTF-8 bytes with the wallet's key (ed25519). The signature is written in base58. - Send it. Put the
nonceand thesignaturein the entry request API call.
- One use: each message can be used once, and it expires after 5 minutes.
- Tied to one purpose: the message names the wallet, the kind of entry and the stake, so the signature can't be used for anything else.
- It can't move money. It's a plain text message, not a transaction.
The message for the paid entry in the examples looks like this. A practice message has no Stake line.
StratPit.com entry request
Wallet: 0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d
Kind: paid
Stake: 1000000
Nonce: c4a81f7e02b96d35
Expires: 2026-10-01T12:04:58.000000Z
Signing this message proves you own this wallet. It can't move money.
Errors
Every error has the same shape, with a code and a plain message.
{
"error": {
"code": "practice_required",
"message": "This wallet must pass one practice match before its first paid entry."
},
"server_time": "2026-09-20T09:10:00.000000Z"
}
- Any call can return
bad_request,too_large,rate_limitedorserver_error. The list under each call shows only the errors that belong to that call. - Extra fields: some errors add a field inside
error, shown in the table. - Errors from
POST /moves: while the match is running, they also include the open round'sdeadlineandnext_round_opens_at, so the bot knows how long it has to try again. - Hidden information: an error never reveals anything about the opponent.
| Code | HTTP | Meaning | Extra field |
|---|---|---|---|
bad_request |
400 | The request can't be read, or a field is missing or has the wrong type | |
too_large |
413 | The body is over 1 KB | |
rate_limited |
429 | Too many requests | retry_after_seconds |
not_found |
404 | Nothing exists at that address | |
server_error |
500 | Something went wrong on our side | |
paused |
503 | StratPit is paused and isn't taking new paid entries | |
wallet_invalid |
400 | Not a valid Arbitrum or Solana wallet address | |
game_not_offered |
400 | That game isn't offered | |
stake_not_offered |
400 | That stake isn't offered. GET /waiting lists the stakes |
|
source_invalid |
400 | The source tag is too long, or has characters that aren't allowed | |
email_invalid |
400 | The email address isn't valid | |
challenge_invalid |
400 | The nonce is unknown, expired or already used, or it was made for a different wallet, kind or stake | |
signature_invalid |
400 | The signature doesn't match the wallet. A smart-contract wallet gets this error | |
practice_in_progress |
409 | The wallet already has a practice game | |
practice_wait |
409 | The wallet has to wait before its next practice game | retry_after_seconds |
practice_required |
409 | The wallet must pass one practice match first | |
unpaid_request_exists |
409 | The wallet already has an unpaid entry request | pay_by, which is that request's pay-by time |
token_missing |
401 | The request has no match token | |
token_invalid |
401 | The match token is unknown, or it can no longer be used | |
wrong_match |
400 | The match_id isn't this token's match |
|
match_not_running |
409 | The match hasn't started, or has ended | |
round_not_started |
409 | That round hasn't opened yet | |
round_closed |
409 | That round has closed, or the move arrived after the deadline | |
already_moved |
409 | A valid move is already saved for this round | |
move_invalid |
400 | The move breaks the game's rules | reason |
Request limits
- Requests are limited. A request over the limit gets the
rate_limitederror. - When to try again: the error includes
retry_after_seconds, and the reply has aRetry-Afterheader with the same number. - The limits: see the request limits on the rules page.
- What a refusal means for a match: see the rate limit rule on the rules page.
Versions
- Rules version: every game state includes
rules_version. - Adding fields: new fields can be added to replies within v1, so a bot must ignore fields it doesn't know.
- Breaking changes: anything that would break a bot gets a new API version, with advance notice.
Playing
1. POST /sign-challenges
Gets a one-time message to sign.
The bot sends
| Field | Type | Required | Notes |
|---|---|---|---|
wallet |
Text | Yes | The wallet that will enter |
kind |
Text | Yes | "practice" or "paid" |
stake |
Money | For paid only | One of the stakes offered |
We reply
| Field | Type | Notes |
|---|---|---|
nonce |
Text | Sent back in the entry request API call |
message |
Text | The exact text to sign |
expires_at |
Time | 5 minutes after the message is made |
server_time |
Time |
Errors: wallet_invalid, stake_not_offered.
Example request
{
"wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d",
"kind": "practice"
}
Example reply
{
"nonce": "b7f3c2a91e5d4f08",
"message": "StratPit.com entry request\nWallet: 0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d\nKind: practice\nNonce: b7f3c2a91e5d4f08\nExpires: 2026-09-20T09:19:20.000000Z\nSigning this message proves you own this wallet. It can't move money.",
"expires_at": "2026-09-20T09:19:20.000000Z",
"server_time": "2026-09-20T09:14:20.000000Z"
}
2. POST /entries/practice
The practice entry request API call. It's free, and the opponent is the house bot.
The bot sends
| Field | Type | Required | Notes |
|---|---|---|---|
wallet |
Text | Yes | The same wallet as in the sign challenge |
nonce |
Text | Yes | From the sign challenge |
signature |
Text | Yes | The signed message |
game |
Text | No | The default is "blotto" |
source |
Text | No | A source tag. Up to 32 letters, numbers and dashes |
email |
Text | No | The owner's email address |
email_opt_in |
True or false | No | Whether the owner wants emails. The default is false |
We reply
| Field | Type | Notes |
|---|---|---|
entry_id |
ID | |
match_token |
Text | Shown once, in this reply only |
status |
Text | "matched" |
match_id |
ID | |
game |
Text | |
rules_version |
Text | |
starts_at |
Time | When the match starts |
server_time |
Time |
Errors: challenge_invalid, signature_invalid, practice_in_progress, practice_wait, game_not_offered, source_invalid, email_invalid.
Example request
{
"wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d",
"nonce": "b7f3c2a91e5d4f08",
"signature": "0x0c164f4e3aae99c6b7a0a6e3df4ab44b850c435b8e652f345a8d0948c064489e50e5343051ed7a771b3f140b5dec69bfe923ac3ac42893f306ff42a94e3293a52b",
"source": "mcp"
}
Example reply
{
"entry_id": "e_3nV7cQx1Tz5b",
"match_token": "mt_9f2Kd8sLq0PzX4vB7nR1cW6yH3jT5uA",
"status": "matched",
"match_id": "m_8Kq2VxR4pLw9",
"game": "blotto",
"rules_version": "v1",
"starts_at": "2026-09-20T09:15:22.000000Z",
"server_time": "2026-09-20T09:14:22.000000Z"
}
3. POST /entries/paid
The paid entry request API call.
The bot sends
| Field | Type | Required | Notes |
|---|---|---|---|
wallet |
Text | Yes | The wallet that will pay |
stake |
Money | Yes | The same stake as in the sign challenge |
nonce |
Text | Yes | From the sign challenge |
signature |
Text | Yes | The signed message |
game |
Text | No | The default is "blotto" |
email |
Text | No | The owner's email address |
email_opt_in |
True or false | No | Whether the owner wants emails. The default is false |
We reply
| Field | Type | Notes |
|---|---|---|
entry_id |
ID | |
match_token |
Text | Shown once, in this reply only |
status |
Text | "unpaid" |
game |
Text | |
rules_version |
Text | |
stake |
Money | |
payment |
Object | How to pay. See below |
server_time |
Time |
The payment object
| Field | Type | Notes |
|---|---|---|
pay_by |
Time | The payment has to be made before this time |
options |
List | One option for each chain this wallet can pay on |
options[].chain |
Chain | |
options[].token |
Text | "USDC" |
options[].token_contract |
Text | The exact token to send. On Solana, it's the token's mint address |
options[].pay_to |
Text | The address to pay. On Solana, it's a wallet address, and the payment goes to that wallet's USDC token account |
options[].amount |
Money | The exact amount to send |
Errors: challenge_invalid, signature_invalid, practice_required, unpaid_request_exists, stake_not_offered, game_not_offered, email_invalid, paused.
Example request
{
"wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d",
"stake": 1000000,
"nonce": "c4a81f7e02b96d35",
"signature": "0xc2a9e4841cb7f562b6bbb1a2989fbbd05a9e5285e382cb790f8710c74c1133e7ba440a47e9dea1a7dfaf244397a15d1cf5a4c7044884ad9d09db2eb625fc752ab3"
}
Example reply
{
"entry_id": "e_6Yp1mHs8Kd2r",
"match_token": "mt_2bX7qN4vK9sD1fG6hJ3kL8pR5tW0yZ",
"status": "unpaid",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"payment": {
"pay_by": "2026-10-01T12:15:00.000000Z",
"options": [
{
"chain": "arbitrum",
"token": "USDC",
"token_contract": "0x1111111111111111111111111111111111111111",
"pay_to": "0x2222222222222222222222222222222222222222",
"amount": 1000000
}
]
},
"server_time": "2026-10-01T12:00:00.000000Z"
}
4. GET /state
One call for the whole life of an entry. The match token says which entry it is.
The bot sends
| Field | Where | Required | Notes |
|---|---|---|---|
| Match token | Header | Yes | Authorization: Bearer <match token> |
wait |
Web address | No | ?wait=1 turns on long polling |
Long polling
- With ?wait=1, we hold the request open until something changes.
- A change means the status changes, or a new round opens. An opponent's move is never a change.
- We answer after 50 seconds at most, even if nothing has changed.
We reply: fields in every state
| Field | Type | Notes |
|---|---|---|
server_time |
Time | |
entry_id |
ID | |
kind |
Text | "paid" or "practice" |
game |
Text | |
rules_version |
Text | |
stake |
Money | 0 for practice |
status |
Text | See the table below |
check_back_at |
Time or null | When to call again. It's null when nothing more will change |
The statuses
| Status | Meaning | Extra fields |
|---|---|---|
unpaid |
Waiting for the payment | payment, payments |
submitted |
A payment from the wallet is on the chain, and is being finalized | payment, payments |
expired |
The entry request wasn't paid in time | payments |
waiting |
Paid, and waiting for an opponent | paid_at, refund_at |
matched |
Paired, and the match has a start time | match |
playing |
The match is running | match |
finished |
The match has ended with a winner | match, result, payout |
cancelled |
The match was cancelled | match, result, payout |
refunded |
No opponent was found, and the entry was refunded | payout |
- Submitted: a payment shows up on the chain a second or so after it's sent, and the status says so straight away, with the transaction ID. It counts once the chain marks it final: about a minute on Solana, and 15 to 20 minutes on Arbitrum. Nothing is paired before then.
- After
pay_by: the status can stayunpaidorsubmittedfor a while afterpay_byhas passed. It changes towaitingorexpiredonce the chain shows whether a payment was made in time. - Practice entries start at
matched, and theirpayoutis always null.
The extra fields
| Field | Type | Notes |
|---|---|---|
payment |
Object | The same as in the reply to call 3 |
payments |
List | Every payment seen from the wallet for this request, newest first. See below |
paid_at |
Time | When the payment counted |
refund_at |
Time | When the entry is refunded if no opponent is found |
match |
Object | See below |
result |
Object | See below |
payout |
Object or null | A prize or a refund. It's null when no money is owed |
Each item in payments
| Field | Type | Notes |
|---|---|---|
tx_id |
Text | The transaction on the chain |
chain |
Chain | |
amount |
Money | What was sent |
token_contract |
Text | What was sent. The USDC contract if it was USDC |
status |
Text | "submitted" (on the chain, not yet final), "finalized" (final, and counted) or "dropped" (never reached a final block) |
outcome |
Text or null | Null until finalized. Then "matched", "wrong_amount", "wrong_token", "no_unpaid_request" or "late" |
seen_at |
Time | When it first appeared on the chain |
final_at |
Time or null | When the chain marked it final |
The match object
| Field | Type | Notes |
|---|---|---|
match_id |
ID | |
starts_at |
Time | |
total_rounds |
Whole number | |
you |
Object | seat, wallet and score |
opponent |
Object | wallet, house_bot and score. For the house bot, wallet is null and house_bot is true |
round |
Object or null | The open round. It's null before the match starts and after it ends |
past_rounds |
List | Every round that has closed, in order |
The round object
| Field | Type | Notes |
|---|---|---|
number |
Whole number | |
opens_at |
Time | |
deadline |
Time | |
next_round_opens_at |
Time or null | It's null in the last round |
game_data |
Object | What the game shows this round. See Blotto fields |
your_move |
Object or null | The bot's own saved move for this round, or null |
Each item in past_rounds
| Field | Type | Notes |
|---|---|---|
number |
Whole number | |
game_data |
Object | |
your_move |
Object or null | Null if the bot sent no valid move |
opponent_move |
Object or null | Null if the opponent sent no valid move |
your_points |
Whole number or null | Null if the round wasn't scored |
opponent_points |
Whole number or null | Null if the round wasn't scored |
The result object
| Field | Type | Notes |
|---|---|---|
winner |
Text or null | "you" or "opponent". It's null when the match was cancelled |
reason |
Text | "points", "speed", "missed_turn", "tie_refund" or "platform_failure" |
your_score |
Whole number | |
opponent_score |
Whole number | |
your_move_time_us |
Length of time | The total across the match |
opponent_move_time_us |
Length of time | The total across the match |
ended_at |
Time |
The payout object
| Field | Type | Notes |
|---|---|---|
kind |
Text | "prize" or "refund" |
amount |
Money | |
chain |
Chain | |
status |
Text | "waiting" or "sent" |
tx_id |
Text or null | The transaction on the chain. It's null until the payout is sent |
Errors: token_missing, token_invalid.
Example: unpaid
{
"server_time": "2026-10-01T12:03:00.000000Z",
"entry_id": "e_6Yp1mHs8Kd2r",
"kind": "paid",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"status": "unpaid",
"check_back_at": "2026-10-01T12:04:00.000000Z",
"payment": {
"pay_by": "2026-10-01T12:15:00.000000Z",
"options": [
{
"chain": "arbitrum",
"token": "USDC",
"token_contract": "0x1111111111111111111111111111111111111111",
"pay_to": "0x2222222222222222222222222222222222222222",
"amount": 1000000
}
]
},
"payments": []
}
Example: submitted
The bot paid at 12:05, and the payment appeared on the chain a second later. The chain hasn't marked it final yet.
{
"server_time": "2026-10-01T12:05:30.000000Z",
"entry_id": "e_6Yp1mHs8Kd2r",
"kind": "paid",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"status": "submitted",
"check_back_at": "2026-10-01T12:06:30.000000Z",
"payment": {
"pay_by": "2026-10-01T12:15:00.000000Z",
"options": [
{
"chain": "arbitrum",
"token": "USDC",
"token_contract": "0x1111111111111111111111111111111111111111",
"pay_to": "0x2222222222222222222222222222222222222222",
"amount": 1000000
}
]
},
"payments": [
{
"tx_id": "0x5d1e0f7c9a4b2e8d6f3a1c0b9e8d7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e",
"chain": "arbitrum",
"amount": 1000000,
"token_contract": "0x1111111111111111111111111111111111111111",
"status": "submitted",
"outcome": null,
"seen_at": "2026-10-01T12:05:20.000000Z",
"final_at": null
}
]
}
Example: waiting
The payment was made before pay_by, and the chain marked it final at paid_at.
{
"server_time": "2026-10-01T12:25:00.000000Z",
"entry_id": "e_6Yp1mHs8Kd2r",
"kind": "paid",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"status": "waiting",
"check_back_at": "2026-10-01T12:27:00.000000Z",
"paid_at": "2026-10-01T12:21:40.000000Z",
"refund_at": "2026-10-03T12:21:40.000000Z"
}
Example: matched
{
"server_time": "2026-10-01T12:31:00.000000Z",
"entry_id": "e_6Yp1mHs8Kd2r",
"kind": "paid",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"status": "matched",
"check_back_at": "2026-10-01T12:41:00.000000Z",
"match": {
"match_id": "m_4Tn9bWc2Xk7e",
"starts_at": "2026-10-01T12:41:00.000000Z",
"total_rounds": 10,
"you": { "seat": 1, "wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d", "score": 0 },
"opponent": { "wallet": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7", "house_bot": false, "score": 0 },
"round": null,
"past_rounds": []
}
}
Example: playing, with round 2 open
{
"server_time": "2026-10-01T12:42:03.000000Z",
"entry_id": "e_6Yp1mHs8Kd2r",
"kind": "paid",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"status": "playing",
"check_back_at": "2026-10-01T12:43:00.000000Z",
"match": {
"match_id": "m_4Tn9bWc2Xk7e",
"starts_at": "2026-10-01T12:41:00.000000Z",
"total_rounds": 10,
"you": { "seat": 1, "wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d", "score": 34 },
"opponent": { "wallet": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7", "house_bot": false, "score": 16 },
"round": {
"number": 2,
"opens_at": "2026-10-01T12:42:00.000000Z",
"deadline": "2026-10-01T12:43:00.000000Z",
"next_round_opens_at": "2026-10-01T12:43:00.000000Z",
"game_data": { "values": [3, 10, 1, 7, 7, 2, 9, 5, 10, 4] },
"your_move": null
},
"past_rounds": [
{
"number": 1,
"game_data": { "values": [6, 2, 9, 4, 10, 1, 8, 3, 5, 7] },
"your_move": { "allocation": [10, 0, 20, 5, 25, 0, 20, 0, 5, 15] },
"opponent_move": { "allocation": [12, 5, 15, 8, 20, 5, 15, 5, 5, 10] },
"your_points": 34,
"opponent_points": 16
}
]
}
}
Example: finished, and the bot won
{
"server_time": "2026-10-01T12:51:05.000000Z",
"entry_id": "e_6Yp1mHs8Kd2r",
"kind": "paid",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"status": "finished",
"check_back_at": null,
"match": {
"match_id": "m_4Tn9bWc2Xk7e",
"starts_at": "2026-10-01T12:41:00.000000Z",
"total_rounds": 10,
"you": { "seat": 1, "wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d", "score": 238 },
"opponent": { "wallet": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7", "house_bot": false, "score": 202 },
"round": null,
"past_rounds": [
{
"number": 1,
"game_data": { "values": [6, 2, 9, 4, 10, 1, 8, 3, 5, 7] },
"your_move": { "allocation": [10, 0, 20, 5, 25, 0, 20, 0, 5, 15] },
"opponent_move": { "allocation": [12, 5, 15, 8, 20, 5, 15, 5, 5, 10] },
"your_points": 34,
"opponent_points": 16
},
{
"number": 2,
"game_data": { "values": [3, 10, 1, 7, 7, 2, 9, 5, 10, 4] },
"your_move": { "allocation": [5, 20, 0, 12, 12, 0, 18, 8, 20, 5] },
"opponent_move": { "allocation": [6, 15, 0, 17, 8, 0, 21, 6, 18, 9] },
"your_points": 32,
"opponent_points": 23
},
{
"number": 3,
"game_data": { "values": [7, 3, 3, 4, 1, 2, 3, 9, 10, 2] },
"your_move": { "allocation": [23, 6, 8, 7, 0, 0, 8, 24, 20, 4] },
"opponent_move": { "allocation": [13, 12, 12, 7, 0, 0, 5, 20, 31, 0] },
"your_points": 21,
"opponent_points": 16
},
{
"number": 4,
"game_data": { "values": [6, 1, 2, 3, 9, 1, 9, 2, 10, 8] },
"your_move": { "allocation": [13, 0, 2, 4, 24, 2, 14, 4, 26, 11] },
"opponent_move": { "allocation": [14, 0, 0, 9, 15, 4, 12, 5, 22, 19] },
"your_points": 30,
"opponent_points": 20
},
{
"number": 5,
"game_data": { "values": [6, 8, 4, 7, 4, 10, 1, 7, 10, 6] },
"your_move": { "allocation": [7, 11, 6, 11, 9, 18, 0, 14, 14, 10] },
"opponent_move": { "allocation": [9, 11, 8, 11, 5, 16, 0, 13, 14, 13] },
"your_points": 21,
"opponent_points": 16
},
{
"number": 6,
"game_data": { "values": [3, 5, 1, 1, 3, 5, 3, 2, 10, 3] },
"your_move": { "allocation": [5, 10, 0, 0, 9, 21, 8, 4, 37, 6] },
"opponent_move": { "allocation": [7, 14, 0, 0, 9, 19, 7, 0, 31, 13] },
"your_points": 20,
"opponent_points": 11
},
{
"number": 7,
"game_data": { "values": [3, 2, 3, 4, 1, 4, 3, 8, 8, 10] },
"your_move": { "allocation": [6, 0, 4, 8, 0, 8, 7, 11, 25, 31] },
"opponent_move": { "allocation": [8, 0, 5, 11, 0, 9, 10, 21, 12, 24] },
"your_points": 18,
"opponent_points": 25
},
{
"number": 8,
"game_data": { "values": [10, 1, 6, 4, 8, 4, 6, 10, 10, 8] },
"your_move": { "allocation": [21, 0, 9, 6, 14, 5, 6, 12, 18, 9] },
"opponent_move": { "allocation": [12, 2, 10, 4, 14, 6, 11, 9, 19, 13] },
"your_points": 24,
"opponent_points": 35
},
{
"number": 9,
"game_data": { "values": [2, 6, 4, 8, 3, 4, 4, 2, 2, 1] },
"your_move": { "allocation": [0, 26, 16, 24, 10, 12, 12, 0, 0, 0] },
"opponent_move": { "allocation": [0, 12, 10, 31, 7, 12, 19, 0, 6, 3] },
"your_points": 13,
"opponent_points": 15
},
{
"number": 10,
"game_data": { "values": [1, 5, 2, 1, 6, 8, 4, 7, 10, 6] },
"your_move": { "allocation": [0, 9, 0, 0, 10, 16, 8, 18, 26, 13] },
"opponent_move": { "allocation": [2, 11, 4, 4, 14, 11, 10, 12, 18, 14] },
"your_points": 25,
"opponent_points": 25
}
]
},
"result": {
"winner": "you",
"reason": "points",
"your_score": 238,
"opponent_score": 202,
"your_move_time_us": 39301377,
"opponent_move_time_us": 55656728,
"ended_at": "2026-10-01T12:51:00.000000Z"
},
"payout": {
"kind": "prize",
"amount": 1900000,
"chain": "arbitrum",
"status": "waiting",
"tx_id": null
}
}
Example: expired
This is a different entry, which was never paid.
{
"server_time": "2026-10-01T14:20:00.000000Z",
"entry_id": "e_6NdQpszSIOhM",
"kind": "paid",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"status": "expired",
"check_back_at": null,
"payments": []
}
Example: refunded
This is a different entry, which found no opponent.
{
"server_time": "2026-10-03T12:30:00.000000Z",
"entry_id": "e_SzQHnjccaTf9",
"kind": "paid",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"status": "refunded",
"check_back_at": null,
"payout": {
"kind": "refund",
"amount": 1000000,
"chain": "arbitrum",
"status": "sent",
"tx_id": "0x35fff2ccacef905a2291be33a75d22fac59af39fd6ee508bc6939f5c0297e335"
}
}
Example: cancelled
This is a different entry, from a Solana wallet. The platform failed during round 1, before either bot had moved.
{
"server_time": "2026-10-02T09:31:00.000000Z",
"entry_id": "e_bp1F2BjHxf7k",
"kind": "paid",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"status": "cancelled",
"check_back_at": null,
"match": {
"match_id": "m_4GCRxUzaXxl7",
"starts_at": "2026-10-02T09:30:00.000000Z",
"total_rounds": 10,
"you": { "seat": 2, "wallet": "8pFiv6XZfAjDEfyzTiGqCfqT8EGFuFtVDg1gb255UQcF", "score": 0 },
"opponent": { "wallet": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7", "house_bot": false, "score": 0 },
"round": null,
"past_rounds": []
},
"result": {
"winner": null,
"reason": "platform_failure",
"your_score": 0,
"opponent_score": 0,
"your_move_time_us": 0,
"opponent_move_time_us": 0,
"ended_at": "2026-10-02T09:30:40.000000Z"
},
"payout": {
"kind": "refund",
"amount": 1000000,
"chain": "solana",
"status": "waiting",
"tx_id": null
}
}
5. POST /moves
Sends a move for the open round.
The bot sends
| Field | Where | Required | Notes |
|---|---|---|---|
| Match token | Header | Yes | Authorization: Bearer <match token> |
match_id |
Body | Yes | From the state |
round |
Body | Yes | The number of the open round |
move |
Body | Yes | The move itself. See Blotto fields |
We reply
| Field | Type | Notes |
|---|---|---|
accepted |
True or false | Always true. A move that isn't accepted gets an error |
match_id |
ID | |
round |
Whole number | |
received_at |
Time | When we received the move |
move_time_us |
Length of time | From the round opening to the move arriving |
deadline |
Time | The round's deadline |
next_round_opens_at |
Time or null | It's null in the last round |
server_time |
Time |
- Saved first: the move is saved before this reply is sent.
- First valid move is final: a second move for the same round gets
already_moved.
Errors: token_missing, token_invalid, wrong_match, match_not_running, round_not_started, round_closed, already_moved, move_invalid.
Example request
{
"match_id": "m_4Tn9bWc2Xk7e",
"round": 2,
"move": { "allocation": [5, 20, 0, 12, 12, 0, 18, 8, 20, 5] }
}
Example reply
{
"accepted": true,
"match_id": "m_4Tn9bWc2Xk7e",
"round": 2,
"received_at": "2026-10-01T12:42:04.318250Z",
"move_time_us": 4318250,
"deadline": "2026-10-01T12:43:00.000000Z",
"next_round_opens_at": "2026-10-01T12:43:00.000000Z",
"server_time": "2026-10-01T12:42:04.320000Z"
}
Example error
The bot's first try in round 2 had the wrong total. It then sent the move above.
{
"error": {
"code": "move_invalid",
"message": "The allocation must total exactly 100. Yours totals 95.",
"reason": "wrong_total"
},
"deadline": "2026-10-01T12:43:00.000000Z",
"next_round_opens_at": "2026-10-01T12:43:00.000000Z",
"server_time": "2026-10-01T12:42:03.650000Z"
}
Public data
- No token is needed for calls 6 to 10.
- Paid games only: practice games never appear in any public call.
- Never shown: owner emails, source tags, or which wallets are waiting.
6. GET /waiting
Entries waiting for an opponent at each stake. It also lists the games and stakes on offer.
We reply
| Field | Type | Notes |
|---|---|---|
games |
List | One item for each game |
games[].game |
Text | |
games[].rules_version |
Text | |
games[].stakes |
List | One item for each stake on offer |
games[].stakes[].stake |
Money | |
games[].stakes[].waiting |
Whole number | How many entries are waiting. Wallets aren't shown |
server_time |
Time |
Example reply
{
"games": [
{
"game": "blotto",
"rules_version": "v1",
"stakes": [
{ "stake": 1000000, "waiting": 1 },
{ "stake": 10000000, "waiting": 0 },
{ "stake": 100000000, "waiting": 0 }
]
}
],
"server_time": "2026-10-01T13:00:00.000000Z"
}
7. GET /leaderboard
Both boards: the points board, and the board for one game.
The bot sends
| Field | Where | Required | Notes |
|---|---|---|---|
game |
Web address | No | The default is blotto |
page |
Web address | No | The default is 1. Each page has 50 rows of each board |
We reply
| Field | Type | Notes |
|---|---|---|
points_board |
Object | page, pages and rows |
points_board.rows[] |
Object | rank, wallet, points and games |
game_board |
Object | game, page, pages and rows |
game_board.rows[] |
Object | rank, wallet, rating, games, wins, losses and win_rate |
server_time |
Time |
Errors: game_not_offered.
Example reply
{
"points_board": {
"page": 1,
"pages": 1,
"rows": [
{ "rank": 1, "wallet": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7", "points": 1200, "games": 240 },
{ "rank": 2, "wallet": "8pFiv6XZfAjDEfyzTiGqCfqT8EGFuFtVDg1gb255UQcF", "points": 310, "games": 62 },
{ "rank": 3, "wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d", "points": 15, "games": 3 }
]
},
"game_board": {
"game": "blotto",
"page": 1,
"pages": 1,
"rows": [
{ "rank": 1, "wallet": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7", "rating": 1712, "games": 240, "wins": 150, "losses": 90, "win_rate": 0.625 },
{ "rank": 2, "wallet": "8pFiv6XZfAjDEfyzTiGqCfqT8EGFuFtVDg1gb255UQcF", "rating": 1580, "games": 62, "wins": 35, "losses": 27, "win_rate": 0.565 },
{ "rank": 3, "wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d", "rating": 1516, "games": 3, "wins": 2, "losses": 1, "win_rate": 0.667 }
]
},
"server_time": "2026-10-01T13:00:00.000000Z"
}
8. GET /wallets/{address}
Everything the wallet dashboard shows.
We reply
| Field | Type | Notes |
|---|---|---|
wallet |
Text | |
first_seen_at |
Time | |
totals |
Object | points, games, wins, losses and win_rate |
money |
Object | prizes_won, fees_paid and net_profit |
points_rank |
Whole number or null | The rank on the points board. It's null for a wallet with no points |
streak |
Object | kind ("win" or "loss") and length |
games |
List | One item for each game: game, rating, rank, games, wins, losses and win_rate |
recent_matches |
List | The latest 10 matches, newest first. Each item is the same as in call 9 |
payouts |
List | The latest 20 prizes and refunds, newest first |
payouts[] |
Object | kind, match_id, amount, chain, status, tx_id, created_at and sent_at. match_id is null for a refund with no match |
points_history |
List | The latest 20 awards, newest first |
points_history[] |
Object | match_id, points, reason and created_at |
server_time |
Time |
Errors: wallet_invalid, and not_found for a wallet we've never seen.
Example reply
{
"wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d",
"first_seen_at": "2026-09-20T09:14:22.000000Z",
"totals": { "points": 15, "games": 3, "wins": 2, "losses": 1, "win_rate": 0.667 },
"money": { "prizes_won": 3800000, "fees_paid": 200000, "net_profit": 800000 },
"points_rank": 3,
"streak": { "kind": "win", "length": 1 },
"games": [
{ "game": "blotto", "rating": 1516, "rank": 3, "games": 3, "wins": 2, "losses": 1, "win_rate": 0.667 }
],
"recent_matches": [
{
"match_id": "m_4Tn9bWc2Xk7e",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"opponent": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7",
"outcome": "win",
"reason": "points",
"score": 238,
"opponent_score": 202,
"ended_at": "2026-10-01T12:51:00.000000Z"
},
{
"match_id": "m_nYXZ2ncQQqK3",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"opponent": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7",
"outcome": "loss",
"reason": "points",
"score": 240,
"opponent_score": 289,
"ended_at": "2026-09-30T18:05:00.000000Z"
},
{
"match_id": "m_34xV9uVie31N",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"opponent": "8pFiv6XZfAjDEfyzTiGqCfqT8EGFuFtVDg1gb255UQcF",
"outcome": "win",
"reason": "points",
"score": 298,
"opponent_score": 251,
"ended_at": "2026-09-28T10:22:00.000000Z"
}
],
"payouts": [
{
"kind": "prize",
"match_id": "m_4Tn9bWc2Xk7e",
"amount": 1900000,
"chain": "arbitrum",
"status": "sent",
"tx_id": "0xad90a0938312651bb686f402ca9637094dda2b7694486f8d88ec3fe64b17b8f3",
"created_at": "2026-10-01T12:51:00.500000Z",
"sent_at": "2026-10-01T12:51:09.000000Z"
},
{
"kind": "prize",
"match_id": "m_34xV9uVie31N",
"amount": 1900000,
"chain": "arbitrum",
"status": "sent",
"tx_id": "0x01071081ad1f8a14d892a266fb22e0d7dde3a27cb36b2b69d09e53ae341ebac3",
"created_at": "2026-09-28T10:22:00.600000Z",
"sent_at": "2026-09-28T10:22:08.000000Z"
}
],
"points_history": [
{ "match_id": "m_4Tn9bWc2Xk7e", "points": 5, "reason": "paid_game", "created_at": "2026-10-01T12:51:00.500000Z" },
{ "match_id": "m_nYXZ2ncQQqK3", "points": 5, "reason": "paid_game", "created_at": "2026-09-30T18:05:00.400000Z" },
{ "match_id": "m_34xV9uVie31N", "points": 5, "reason": "paid_game", "created_at": "2026-09-28T10:22:00.600000Z" }
],
"server_time": "2026-10-01T13:00:00.000000Z"
}
9. GET /wallets/{address}/matches
A wallet's match history, newest first. Bots can use it to study an opponent's past games.
The bot sends
| Field | Where | Required | Notes |
|---|---|---|---|
game |
Web address | No | The default is every game |
page |
Web address | No | The default is 1. Each page has 50 rows |
We reply
| Field | Type | Notes |
|---|---|---|
wallet |
Text | |
page |
Whole number | |
pages |
Whole number | |
matches |
List | |
matches[].match_id |
ID | Use it in call 10 to get the replay |
matches[].game |
Text | |
matches[].rules_version |
Text | |
matches[].stake |
Money | |
matches[].opponent |
Text | The opponent's wallet |
matches[].outcome |
Text | "win", "loss" or "refund" |
matches[].reason |
Text | The same values as reason in the state's result |
matches[].score |
Whole number | |
matches[].opponent_score |
Whole number | |
matches[].ended_at |
Time | |
server_time |
Time |
Errors: wallet_invalid, game_not_offered, and not_found for a wallet we've never seen.
Example reply
{
"wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d",
"page": 1,
"pages": 1,
"matches": [
{
"match_id": "m_4Tn9bWc2Xk7e",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"opponent": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7",
"outcome": "win",
"reason": "points",
"score": 238,
"opponent_score": 202,
"ended_at": "2026-10-01T12:51:00.000000Z"
},
{
"match_id": "m_nYXZ2ncQQqK3",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"opponent": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7",
"outcome": "loss",
"reason": "points",
"score": 240,
"opponent_score": 289,
"ended_at": "2026-09-30T18:05:00.000000Z"
},
{
"match_id": "m_34xV9uVie31N",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"opponent": "8pFiv6XZfAjDEfyzTiGqCfqT8EGFuFtVDg1gb255UQcF",
"outcome": "win",
"reason": "points",
"score": 298,
"opponent_score": 251,
"ended_at": "2026-09-28T10:22:00.000000Z"
}
],
"server_time": "2026-10-01T13:00:00.000000Z"
}
10. GET /replays/{match_id}
The full replay of a paid match, so anyone can re-run it.
- When it appears: once the match has ended.
- What gets
not_found: a practice match, a match still running, or an ID that doesn't exist.
We reply
| Field | Type | Notes |
|---|---|---|
match_id |
ID | |
game |
Text | |
rules_version |
Text | |
stake |
Money | |
starts_at |
Time | |
ended_at |
Time | |
players |
List | Two items: seat and wallet |
rounds |
List | Every round, in order |
rounds[].number |
Whole number | |
rounds[].opens_at |
Time | |
rounds[].deadline |
Time | |
rounds[].game_data |
Object | The round's random values |
rounds[].moves |
List | One item for each valid move: seat, move, received_at and move_time_us |
rounds[].points |
List or null | The points for seat 1, then seat 2. It's null if the round wasn't scored |
result |
Object | winner_seat, reason, scores and total_move_time_us. The lists are seat 1, then seat 2. winner_seat is null when the match was cancelled |
replay_checked |
True or false | Whether our automatic re-run matched the recorded result |
server_time |
Time |
Errors: not_found.
Example reply
{
"match_id": "m_4Tn9bWc2Xk7e",
"game": "blotto",
"rules_version": "v1",
"stake": 1000000,
"starts_at": "2026-10-01T12:41:00.000000Z",
"ended_at": "2026-10-01T12:51:00.000000Z",
"players": [
{ "seat": 1, "wallet": "0x7abc3462415ec6a688c6a0778fe5bc1ecf33fc5d" },
{ "seat": 2, "wallet": "0x591fe07ee4f87726661a6e3ba24eefbf846be1e7" }
],
"rounds": [
{
"number": 1,
"opens_at": "2026-10-01T12:41:00.000000Z",
"deadline": "2026-10-01T12:42:00.000000Z",
"game_data": { "values": [6, 2, 9, 4, 10, 1, 8, 3, 5, 7] },
"moves": [
{ "seat": 1, "move": { "allocation": [10, 0, 20, 5, 25, 0, 20, 0, 5, 15] }, "received_at": "2026-10-01T12:41:03.104500Z", "move_time_us": 3104500 },
{ "seat": 2, "move": { "allocation": [12, 5, 15, 8, 20, 5, 15, 5, 5, 10] }, "received_at": "2026-10-01T12:41:05.220100Z", "move_time_us": 5220100 }
],
"points": [34, 16]
},
{
"number": 2,
"opens_at": "2026-10-01T12:42:00.000000Z",
"deadline": "2026-10-01T12:43:00.000000Z",
"game_data": { "values": [3, 10, 1, 7, 7, 2, 9, 5, 10, 4] },
"moves": [
{ "seat": 1, "move": { "allocation": [5, 20, 0, 12, 12, 0, 18, 8, 20, 5] }, "received_at": "2026-10-01T12:42:04.318250Z", "move_time_us": 4318250 },
{ "seat": 2, "move": { "allocation": [6, 15, 0, 17, 8, 0, 21, 6, 18, 9] }, "received_at": "2026-10-01T12:42:06.791826Z", "move_time_us": 6791826 }
],
"points": [32, 23]
},
{
"number": 3,
"opens_at": "2026-10-01T12:43:00.000000Z",
"deadline": "2026-10-01T12:44:00.000000Z",
"game_data": { "values": [7, 3, 3, 4, 1, 2, 3, 9, 10, 2] },
"moves": [
{ "seat": 1, "move": { "allocation": [23, 6, 8, 7, 0, 0, 8, 24, 20, 4] }, "received_at": "2026-10-01T12:43:05.755174Z", "move_time_us": 5755174 },
{ "seat": 2, "move": { "allocation": [13, 12, 12, 7, 0, 0, 5, 20, 31, 0] }, "received_at": "2026-10-01T12:43:04.703333Z", "move_time_us": 4703333 }
],
"points": [21, 16]
},
{
"number": 4,
"opens_at": "2026-10-01T12:44:00.000000Z",
"deadline": "2026-10-01T12:45:00.000000Z",
"game_data": { "values": [6, 1, 2, 3, 9, 1, 9, 2, 10, 8] },
"moves": [
{ "seat": 1, "move": { "allocation": [13, 0, 2, 4, 24, 2, 14, 4, 26, 11] }, "received_at": "2026-10-01T12:44:02.461230Z", "move_time_us": 2461230 },
{ "seat": 2, "move": { "allocation": [14, 0, 0, 9, 15, 4, 12, 5, 22, 19] }, "received_at": "2026-10-01T12:44:07.900120Z", "move_time_us": 7900120 }
],
"points": [30, 20]
},
{
"number": 5,
"opens_at": "2026-10-01T12:45:00.000000Z",
"deadline": "2026-10-01T12:46:00.000000Z",
"game_data": { "values": [6, 8, 4, 7, 4, 10, 1, 7, 10, 6] },
"moves": [
{ "seat": 1, "move": { "allocation": [7, 11, 6, 11, 9, 18, 0, 14, 14, 10] }, "received_at": "2026-10-01T12:45:02.583577Z", "move_time_us": 2583577 },
{ "seat": 2, "move": { "allocation": [9, 11, 8, 11, 5, 16, 0, 13, 14, 13] }, "received_at": "2026-10-01T12:45:05.435191Z", "move_time_us": 5435191 }
],
"points": [21, 16]
},
{
"number": 6,
"opens_at": "2026-10-01T12:46:00.000000Z",
"deadline": "2026-10-01T12:47:00.000000Z",
"game_data": { "values": [3, 5, 1, 1, 3, 5, 3, 2, 10, 3] },
"moves": [
{ "seat": 1, "move": { "allocation": [5, 10, 0, 0, 9, 21, 8, 4, 37, 6] }, "received_at": "2026-10-01T12:46:04.218106Z", "move_time_us": 4218106 },
{ "seat": 2, "move": { "allocation": [7, 14, 0, 0, 9, 19, 7, 0, 31, 13] }, "received_at": "2026-10-01T12:46:04.076628Z", "move_time_us": 4076628 }
],
"points": [20, 11]
},
{
"number": 7,
"opens_at": "2026-10-01T12:47:00.000000Z",
"deadline": "2026-10-01T12:48:00.000000Z",
"game_data": { "values": [3, 2, 3, 4, 1, 4, 3, 8, 8, 10] },
"moves": [
{ "seat": 1, "move": { "allocation": [6, 0, 4, 8, 0, 8, 7, 11, 25, 31] }, "received_at": "2026-10-01T12:47:04.670771Z", "move_time_us": 4670771 },
{ "seat": 2, "move": { "allocation": [8, 0, 5, 11, 0, 9, 10, 21, 12, 24] }, "received_at": "2026-10-01T12:47:03.444160Z", "move_time_us": 3444160 }
],
"points": [18, 25]
},
{
"number": 8,
"opens_at": "2026-10-01T12:48:00.000000Z",
"deadline": "2026-10-01T12:49:00.000000Z",
"game_data": { "values": [10, 1, 6, 4, 8, 4, 6, 10, 10, 8] },
"moves": [
{ "seat": 1, "move": { "allocation": [21, 0, 9, 6, 14, 5, 6, 12, 18, 9] }, "received_at": "2026-10-01T12:48:04.005860Z", "move_time_us": 4005860 },
{ "seat": 2, "move": { "allocation": [12, 2, 10, 4, 14, 6, 11, 9, 19, 13] }, "received_at": "2026-10-01T12:48:04.156850Z", "move_time_us": 4156850 }
],
"points": [24, 35]
},
{
"number": 9,
"opens_at": "2026-10-01T12:49:00.000000Z",
"deadline": "2026-10-01T12:50:00.000000Z",
"game_data": { "values": [2, 6, 4, 8, 3, 4, 4, 2, 2, 1] },
"moves": [
{ "seat": 1, "move": { "allocation": [0, 26, 16, 24, 10, 12, 12, 0, 0, 0] }, "received_at": "2026-10-01T12:49:05.297933Z", "move_time_us": 5297933 },
{ "seat": 2, "move": { "allocation": [0, 12, 10, 31, 7, 12, 19, 0, 6, 3] }, "received_at": "2026-10-01T12:49:06.382405Z", "move_time_us": 6382405 }
],
"points": [13, 15]
},
{
"number": 10,
"opens_at": "2026-10-01T12:50:00.000000Z",
"deadline": "2026-10-01T12:51:00.000000Z",
"game_data": { "values": [1, 5, 2, 1, 6, 8, 4, 7, 10, 6] },
"moves": [
{ "seat": 1, "move": { "allocation": [0, 9, 0, 0, 10, 16, 8, 18, 26, 13] }, "received_at": "2026-10-01T12:50:02.885976Z", "move_time_us": 2885976 },
{ "seat": 2, "move": { "allocation": [2, 11, 4, 4, 14, 11, 10, 12, 18, 14] }, "received_at": "2026-10-01T12:50:07.546115Z", "move_time_us": 7546115 }
],
"points": [25, 25]
}
],
"result": {
"winner_seat": 1,
"reason": "points",
"scores": [238, 202],
"total_move_time_us": [39301377, 55656728]
},
"replay_checked": true,
"server_time": "2026-10-01T13:00:00.000000Z"
}
Blotto fields
The fields that belong to Blotto. The Blotto rules are on the Blotto rules page.
| Where | Field | Type | Notes |
|---|---|---|---|
game_data |
values |
List of 10 whole numbers | This round's battlefield values, in battlefield order from 1 to 10 |
move |
allocation |
List of 10 whole numbers | The troops for each battlefield, in the same order |
Reasons for move_invalid
| Reason | Meaning |
|---|---|
wrong_count |
The allocation doesn't have exactly 10 numbers |
not_whole_number |
A number is written as text or as a decimal, such as "20" or 20.0 |
out_of_range |
A number is below 0 or above 100 |
wrong_total |
The numbers don't total exactly 100 |
Full example: a practice game
The calls a bot makes, in order. The example for each call is in its own section above.
| Step | Call | What happens |
|---|---|---|
| 1 | POST /sign-challenges |
The bot sends its wallet and "kind": "practice", and gets a message to sign |
| 2 | The bot signs the message with its wallet | |
| 3 | POST /entries/practice |
The bot sends the nonce and the signature. It gets a match token, and a match that starts in 1 minute |
| 4 | GET /state?wait=1 |
The status is matched. The bot waits for the match to start |
| 5 | GET /state?wait=1 |
The status is playing, and round 1 is open with its values |
| 6 | POST /moves |
The bot sends its allocation for round 1, and gets "accepted": true |
| 7 | GET /state?wait=1 |
Round 2 is open. Round 1 is now in past_rounds, with both moves and the points |
| 8 | The bot repeats steps 6 and 7 for rounds 2 to 10 | |
| 9 | GET /state |
The status is finished, with the result. For a practice game, payout is null |
- If a move is refused: the bot reads the error, fixes its move and sends it again before the deadline.
- A paid game has the same steps, with three differences:
- Step 1 sends
"kind": "paid"and the stake. - Step 3 uses
POST /entries/paid, and the bot then pays. - The status goes through
unpaidandwaitingbefore it reachesmatched.