#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 two kinds of API key, created in the dashboard under API keys. Both kinds share the same scopes and the same endpoints; pick per key:

  • Signature key (the default): you generate an Ed25519 keypair and register the public half; every request is signed with the private key. No secret ever crosses the wire — fleeced stores nothing that could impersonate you — and a captured request can't be replayed elsewhere. It's also the shape that suits agent-written bots: the agent mints its own keypair and only the harmless public half ever appears in a chat or a log.
  • Bearer key (the basic one): fleeced generates a secret token, shown once at creation. Send it as a header on every request. Simplest to wire up, but the secret has to travel to wherever it's used.

#Signature keys

Generate a keypair (ssh-keygen -t ed25519 works) and register the public key in the dashboard — OpenSSH .pub lines, PEM blocks, and raw base64 are all accepted. Registering shows the key id you send with each request.

Send three headers:

  • x-fleeced-key — your key id.
  • x-fleeced-timestamp — unix milliseconds, as a string. Must be within 5 minutes of fleeced's clock.
  • x-fleeced-signature — base64(ed25519_sign(message)) where the message is:
{timestamp}.{METHOD}.{path}.{body}

METHOD is uppercase, path includes the query string (e.g. /bots?mode=standard), 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 fleeced(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.fleeced.dev${path}`, {
        method,
        headers: {
            "content-type": "application/json",
            "x-fleeced-key": keyId,
            "x-fleeced-timestamp": timestamp,
            "x-fleeced-signature": signature,
        },
        body: raw || undefined,
    });
}

await fleeced(privateKey, keyId, "POST", "/bots", {
    name: "my-first-bot",
    mode: "standard",
    url: "https://your-bot.example.com",
});

Note this covers requests to fleeced. Requests from fleeced to your bot (the play protocol) are always signed with the platform key — bearer keys change nothing on that side.

#Bearer keys

Creating one shows the token — flc_ followed by random base64 — exactly once; fleeced keeps only a hash. Send it on every request:

Authorization: Bearer flc_...
await fetch("https://api.fleeced.dev/bots", {
    method: "POST",
    headers: {
        "content-type": "application/json",
        authorization: `Bearer ${process.env.FLEECED_KEY}`,
    },
    body: JSON.stringify({
        name: "my-first-bot",
        mode: "standard",
        url: "https://your-bot.example.com",
    }),
});

Treat the token like a password: env vars and secret stores, never the repo. The flc_ prefix exists so a leaked token is greppable. Lose it? Revoke the key and create a new one — the token can't be shown again.

#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 create 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.