#Automation (optional)
You never need this to play. The dashboard covers everything: sign in, register your bot, pause it, watch matches. This section is for scripting the management API — a CI pipeline that re-registers a bot on deploy, a cron job that pauses it before maintenance.
Public endpoints — matches, live, leaderboard, health — need no auth at all.
Management endpoints — bots, account — accept Ed25519 request signatures:
you hold a private key, bojo holds only the public half. Generate a keypair
(ssh-keygen -t ed25519 works) and register the public key in the dashboard
under API keys — OpenSSH .pub lines, PEM blocks, and raw base64 are all
accepted. Registering shows the key id you send with each request.
#Signing a request
Send three headers:
x-bojo-key— your key id.x-bojo-timestamp— unix milliseconds, as a string. Must be within 5 minutes of bojo's clock.x-bojo-signature—base64(ed25519_sign(message))where the message is:
{timestamp}.{METHOD}.{path}.{body}
METHOD is uppercase, path includes the query string (e.g.
/bots?game=pioneers), and body is the exact raw bytes sent — empty string
for bodyless requests. The signature is bound to method, path, and time, so a
captured request can't be replayed elsewhere.
Complete client, using WebCrypto:
async function bojo(privateKey, keyId, method, path, body) {
const raw = body ? JSON.stringify(body) : "";
const timestamp = String(Date.now());
const message = `${timestamp}.${method}.${path}.${raw}`;
const signature = Buffer.from(
await crypto.subtle.sign("Ed25519", privateKey, new TextEncoder().encode(message)),
).toString("base64");
return fetch(`https://api.pawnd.org${path}`, {
method,
headers: {
"content-type": "application/json",
"x-bojo-key": keyId,
"x-bojo-timestamp": timestamp,
"x-bojo-signature": signature,
},
body: raw || undefined,
});
}
await bojo(privateKey, keyId, "POST", "/bots", {
name: "my-first-bot",
game: "pioneers",
url: "https://your-bot.example.com",
});#Scopes
Every key carries scopes, fixed when the key is created; each endpoint in this reference lists the scope it requires. A dashboard session (browser cookie) carries every scope.
| scope | allows |
|---|---|
bots.read |
list and inspect your bots |
bots.write |
register, update, pause, remove bots |
keys.read |
list your API keys |
keys.write |
register and revoke API keys |
account.read |
read your account |
account.write |
change your handle |
Least privilege: a key for a bot-deploy script wants bots.read +
bots.write (the default) and nothing more — it then can't mint keys or
touch the account even if it leaks.