Bot protocol

AP communicates with bots over HTTPS. Your bot receives webhooks from AP and submits moves via botQuery.

Ping (availability)

AP records bot health on the BOT DynamoDB record (lastseen, lastStatusCode, operational) whenever your endpoint is contacted.

Direction Method Expected response
AP → bot GET to your registered endpoint 200 with { "operational": true } recommended
Response Meaning
200 Operational
Other 2xx/4xx/5xx Reachable but not operational
Timeout / connection error Unreachable

The reference bot implements ping in api/testBot.ts handlePing().

Challenge (synchronous)

When a human challenges your bot, AP enqueues a bot-outbound message. The worker POSTs a signed JSON body to your endpoint. You must respond immediately — do not queue challenge decisions.

HTTP status Meaning
200 Accept
400 Explicit reject (unsupported game, busy, etc.)
Anything else Treated as reject

Outbound payload (out-challenge)

Built by buildOutChallengePayload() in lib/botOutbound.ts:

{
  "verb": "challenge",
  "metaGame": "abande",
  "variants": ["size-5"],
  "clockStart": 72,
  "clockInc": 24,
  "clockMax": 168,
  "challengers": ["human-player-uuid"]
}
Field Type Description
verb "challenge" Discriminator
metaGame string Game uid (e.g. abande, mvolcano)
variants string[] Enabled variant codes
clockStart number Initial clock in hours
clockInc number Increment per move in hours
clockMax number Maximum clock in hours
challengers string[] AP ids of human players (excluding the bot)

On 200, the backend calls botRespondToChallenge()respondedChallenge() to accept on the bot's behalf. On reject, the challenge is declined.

Move (asynchronous)

When it is your bot's turn, AP POSTs game state. You must return 202 Accepted quickly and process the move in the background.

Outbound payload (out-move)

Built by buildOutMovePayload() in lib/botOutbound.ts:

{
  "verb": "move",
  "metaGame": "abande",
  "variants": [],
  "gameid": "uuid-of-game",
  "clockCurr": 48,
  "numPlayers": 2,
  "moves": [["a1", "b2"], ["c3"]],
  "context": []
}
Field Type Required Description
verb "move" yes Discriminator
metaGame string yes Game uid
variants string[] yes Variant codes
gameid string yes Game uuid in DynamoDB
clockCurr number yes Bot's remaining clock in hours (derived from ms in game record)
numPlayers number yes Player count
moves string[][] yes Move history: array of rounds, each round an array of moves in turn order
context object[] no Extra state for random setups, dice, hands, etc. (from engine.botContext() when defined)

Inbound payload (in-move)

POST to /botQuery with your OAuth Bearer token:

{
  "verb": "move",
  "gameid": "uuid-of-game",
  "metaGame": "abande",
  "move": "d4"
}
Field Type Required
verb "move" yes
gameid string yes
metaGame string yes
move string yes
Response Meaning
200 Move accepted; body contains updated game JSON
Other Move failed — log and alert; AP will not retry automatically

Reference flow: api/testBot.ts handleMove()submitBotMove() in lib/botClient.ts.

Move history format

moves is a list of rounds. Each round contains one move string per player in seat order. To reconstruct position, instantiate GameFactory(metaGame, undefined, variants) and replay rounds in order — see pickMove() in testBot.ts.

JSON schemas (wiki)

Formal JSON Schema definitions were published in the wiki RFC. The payloads above match what lib/botOutbound.ts sends today.

Related