#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-signaturebase64(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.