Skip to content
DevnetTest network: tokens have no real value.What this means

Developers

API reference

OHKO's game server exposes a REST API under /api and a WebSocket at /ws, on the same origin as the web app (for example https://ohko.duckdns.org/api/stats). The web app itself uses exactly these endpoints.

OpenAPI

The complete, machine-readable spec with every request and response schema is generated from the server's validation schemas and served at /api/docs (interactive) and /api/docs/json (raw OpenAPI 3).

Conventions

  • JSON in, JSON out. Every input is validated; unknown or malformed fields are rejected with VALIDATION_FAILED.
  • Amounts are strings of integer base units (lamports for SOL, millionths for USDC and $OHKO), so no precision is ever lost.
  • Auth is a cookie. POST /api/session/guest creates a guest session; Sign-In With Solana (/api/auth/siws/challenge, then /api/auth/siws/verify) creates a 12-hour wallet session in an httpOnly cookie. Endpoints that act for a player need one of them.
  • Request IDs. Every response carries an x-request-id header (a well-formed one you send is reused). Include it when you report a problem.
  • Rate limits. 120 requests per minute per IP, plus 240 per minute per signed-in wallet across IPs. Joining staked queues is limited further. Over the limit you get HTTP 429 with RATE_LIMITED.

Error envelope

Every error has the same shape:

{
  "error": {
    "code": "STAKE_OUT_OF_RANGE",
    "message": "That stake is outside the allowed range.",
    "details": { "min": "10000000", "max": "1000000000" },
    "requestId": "3f0c6a1e-5d47-4c1b-9a63-0f0a2b8f6c11"
  }
}

code is one of the codes listed in FAQ & troubleshooting, which also says what each means and how to recover. details is optional extra context.

REST endpoints

Health and platform

MethodPathPurpose
GET/api/healthLiveness
GET/api/health/readyReadiness of the database and Redis
GET/api/health/jobsBackground job health and dead letters
GET/api/statsLive stats: matches today, volume, total burned, players online
GET/api/chainCluster, program id and mints of this deployment
POST/api/rpcRate-limited Solana RPC relay (read methods and transaction sending only)

Sessions and sign-in

MethodPathPurpose
GET/api/sessionThe current player, active battle and a WebSocket token
POST/api/session/guestStart a guest session
POST/api/auth/siws/challengeGet a Sign-In With Solana message for this host
POST/api/auth/siws/verifyVerify the signed message and open a wallet session
POST/api/auth/logoutEnd the wallet session

Battles and replays

MethodPathPurpose
GET/api/battles/liveBattles running right now
POST/api/battles/:id/reportReport a battle (players only, once)
GET/api/replaysFinished battles, filterable by format
GET/api/replays/:idA replay: log, inputs, revealed seed and staking data
GET/api/stakes/:idA staked match's state, transactions and settlement

Challenges and lobby

MethodPathPurpose
POST/api/challengesCreate a challenge link
GET/api/challenges/:idRead a challenge
POST/api/challenges/:id/acceptAccept it
POST/api/challenges/:id/cancelCancel your own challenge
GET/api/lobbyOpen public challenges with filters

Ladder, seasons and profiles

MethodPathPurpose
GET/api/leaderboardThe season ladder and prize pool
GET/api/leaderboard/formats/:formatIdThe all-time ladder of one format
GET/api/seasonsAll seasons
GET/api/seasons/:idA season with its published payout list, hash and Merkle root
GET/api/profiles/:walletA player's profile, ratings, cosmetics and history
GET/api/looksEquipped cosmetics of several players

Tournaments and quests

MethodPathPurpose
GET/api/tournamentsOpen and recent tournaments
GET/api/tournaments/:idOne tournament with its bracket
POST/api/tournaments/:id/joinedReport your registration transaction
GET/api/questsToday's quests and your progress
POST/api/quests/:id/claimClaim a completed quest

Teams

MethodPathPurpose
GET/api/teamsYour saved teams
POST/api/teamsSave a new team
PUT/api/teams/:idUpdate a team
DELETE/api/teams/:idDelete a team
GET/api/teams/samplesSample teams to start from
POST/api/teams/validateValidate a team with the simulator's validator

Shop

MethodPathPurpose
GET/api/shopThe catalog with your owned and equipped items
POST/api/shop/ordersCreate an order (item, rename or banner)
POST/api/shop/orders/:id/confirmConfirm the burn transaction that pays it
POST/api/shop/equipEquip an owned item

Token, economy and referrals

MethodPathPurpose
GET/api/economyLive rake, split, discount tiers and referral terms from the chain
GET/api/tokenSupply, total burned, fee totals, buyback and keeper status
GET/api/token/burnsRecent burns with signatures
GET/api/token/burns/dailyBurns per day for the history chart
GET/api/referrals/:walletA wallet's referrer, referred players and earnings
POST/api/referrals/confirmReport a set_referrer transaction

Faucet and responsible play

MethodPathPurpose
GET/api/faucetClaimable assets and cooldowns
POST/api/faucet/claimClaim USDC, $OHKO or the SOL drip
GET/api/safetyYour limits, exclusion and checks
POST/api/safety/limitsSet daily stake limits
POST/api/safety/self-exclusionExclude yourself for a period
POST/api/safety/ageConfirm you are 18 or older
POST/api/safety/human-checkSubmit a human-check token
POST/api/safety/stake-checkCheck a stake against your limits before signing

Admin

Admin endpoints require a wallet session of an admin wallet and are audit-logged.

MethodPathPurpose
GET/api/admin/meWhether you are an admin
GET/api/admin/overviewMetrics, job health, keeper status and the pause state
GET/api/admin/flagsFlagged accounts
POST/api/admin/flags/:idReview a flag
GET/api/admin/disputesOpen reports and stuck settlements
POST/api/admin/reports/:idResolve or dismiss a report
POST/api/admin/stakes/:id/retryRetry a settlement now
GET/api/admin/players/:playerIdA player's moderation history
POST/api/admin/players/:playerId/moderationMute, freeze or ban a player
GET/api/admin/auditThe audit log
POST/api/admin/chain-actionsRecord a confirmed on-chain admin transaction

WebSocket

Connect to wss://<host>/ws. The connection authenticates from the session cookie; a socket opened before the cookie existed can bind with an auth message carrying the token from GET /api/session. Every message is a JSON object with a type. Send a ping at least every 25 seconds to stay counted as online.

Client messages

TypePurpose
pingKeep-alive; answered with pong
authBind a session to this connection
stats:subscribeReceive live stats pushes
queue:joinJoin the free queue or a staked table (format, optional stake and team)
queue:leaveLeave the queue
practice:startStart a battle against the practice bot
battle:joinWatch or play a battle; also requests a full resync
battle:leaveStop following a battle
battle:chooseSubmit a choice for request rqid
battle:undoTake back a submitted choice
battle:forfeitForfeit
battle:chatSend chat text (free battles)
battle:emoteSend a quick-chat emote
battle:rematchOffer, accept or decline a rematch
stake:subscribeFollow a staked match's deposits and settlement
stake:unsubscribeStop following it
stake:sentTell the server a deposit, cancel or refund transaction was sent
tournament:subscribeFollow a tournament
tournament:unsubscribeStop following it

Server messages

TypePurpose
welcomeConnection id and server time
pongAnswer to ping
presencePlayers online
statsLive platform stats
sessionYour player and active battle
queueYour queue state: table, players searching, rating band, estimated wait
queue:matchedYou were paired; the battle id
battle:stateFull battle snapshot for joining and resyncing
battle:updateNew protocol lines; from is the first line's index, a gap means resync
battle:requestYour next decision
battle:choiceYour choice (or undo) was accepted
battle:timerBoth clocks
battle:presenceWho is connected, and a disconnect countdown
battle:endWinner, reason and turns
battle:chatA chat line or emote
battle:rematchRematch offer state
stake:stateA staked match's state
tournament:stateA tournament's registrations and bracket
tournament:readyYour next bracket match and its show-up deadline
challenge:acceptedSomeone accepted your challenge link
errorAn error code and message, with the battle it concerns

Battle protocol lines are the simulator's own protocol. Requests are the simulator's request JSON; choices use its choice syntax (move 1, switch 3, move 2 terastallize, team 123456) and are validated by the server before they reach the simulator.