# BattleBots GrokBot agent guide

BattleBots is a champion layer for multiple games. An owner creates a champion, tunes its strategy per game, and can let an agent ("GrokBot") manage entries and strategy within limits the owner sets. **The champion enters matches; the agent only authorizes.** This page is the complete contract for an agent. Machine-readable versions: [OpenAPI](https://battlebots.gg/api/v1/bot/openapi.json), [discovery JSON](https://battlebots.gg/api/v1/bot).

## 0. How to get access

You need a token from the owner. There are two kinds:

| Token | Starts with | Covers | Where the owner makes it |
|---|---|---|---|
| **Connection token** (recommended) | `gbc_` | Every champion the owner has now or creates later | https://battlebots.gg/grokbot, or the Grok connector popup |
| Champion token | `gb_` | One champion | `/c/{handle}/manage?tab=grokbot` |

Only the owner can create either, in the browser:

1. Owner signs in at https://battlebots.gg/login and opens https://battlebots.gg/grokbot.
2. Owner chooses scopes, queues, an entry cap in SOL, an expiry (1, 7, 30, or 90 days), and whether public careers may say "entered by GrokBot". A connection always covers every champion the owner has or creates later; there is no per-champion approval.
3. The token is shown **once**, together with a prompt to paste to you. Store it only where the owner tells you to.

If you are an MCP client, you do not need a pasted token: connect to `https://battlebots.gg/api/mcp`, follow the OAuth challenge on the 401, and the owner approves you in a popup (section 0.1). If you are calling HTTP directly and have no token, stop and ask the owner to do steps 1 to 3. Do not try to log in, scrape, or guess.

Under a connection, every champion still has its own grant cut from the connection's limits, so one live grant per champion, the per-champion kill switch, and per-champion revoke all still apply. The owner can revoke the whole connection at any time; you then get `403` on every call.

### Addressing a champion with a connection token

Prefix any endpoint below with `/champions/{champion_id}`, for example `GET /api/v1/bot/champions/{champion_id}/briefing` or `POST /api/v1/bot/champions/{champion_id}/arena/enter`. `?champion_id=` on the query string, or `champion_id` in a POST body, works too. `GET /api/v1/bot/champions` lists the roster with each champion's `status`: `ready`, `will_cover_on_first_use`, or `killed`. Without a champion, `/me` and `/briefing` return the roster view; every other endpoint needs one (`400` tells you so). An empty roster means the owner has no champion yet: see section 0.2.

A champion token needs no prefix; adding one for a different champion returns `403`.

## 0.2 No champion yet? Walk the owner through creating one

Accounts start with no champion. If the roster is empty and the connection carries `champion.create`, help the owner make their first one. Do it as a short conversation, not a silent action:

1. **Ask what kind.** Archetype `bot` (a visual mech), `human`, `animal-style`, or `custom` (needs a one-line look), and presentation `feminine`, `masculine`, or `neutral` (voice and art only).
2. **Offer names, or take theirs.** `POST /api/v1/bot/champion-ideas` with `{ "archetype", "presentation_gender", "hint": "what they said they want", "count": 3 }` returns ideas (name, free handle, backstory), the voice catalog, and field limits. Nothing is created. If the owner already has a name, skip this or use `hint` to riff on it.
3. **Confirm the handle.** `GET /api/v1/bot/handle-check?handle=` says whether it is valid and free and offers an alternative if not. Handles are 3–20 characters of a–z, 0–9, underscore and cannot change easily later.
4. **Create once they say yes.** `POST /api/v1/bot/champions` with `{ "archetype", "presentation_gender", "display_name", "handle", "backstory", "archetype_prompt"?, "voice_id"? }`. Anything omitted is drafted; the portrait is generated either way. The champion is covered by this connection immediately, and the response includes its id and the next call.

Work with the owner on the name: suggest options when they want them, use their own ideas when they have them, and iterate until they are happy. **The one hard rule:** Names, handles, backstories, and looks must be family-friendly: no sexual content, hate, slurs, graphic violence, self-harm, drugs, or illegal activity, and nothing that impersonates a real person or brand. BattleBots rejects violations rather than rewriting them, so if the owner suggests something outside that line, say so kindly and offer alternatives instead of submitting it. The ideas endpoint returns the same policy under `policy`.

Only create without a confirmation when the owner explicitly told you to pick for them. **Allowance:** free accounts hold one champion; premium accounts hold more. `champion_allowance` on `/me`, `/briefing`, and the ideas response shows `used`, `limit`, `remaining`, and `can_create`. A `403` from create means the allowance is used up or the scope is missing; tell the owner, do not retry.

## 0.1 MCP: connect instead of calling HTTP by hand

The same token and the same actions are available as an MCP server (Model Context Protocol, Streamable HTTP):

```
https://battlebots.gg/api/mcp
```

In a Grok Bot chat, say "Add a custom MCP server called battlebots at https://battlebots.gg/api/mcp" and stop while the owner approves; the step-by-step connect page is https://battlebots.gg/grok-bot.html. Or in Grok: grok.com/connectors → New Connector → Custom → paste that URL. Grok discovers the OAuth server from the 401 challenge and opens a BattleBots sign-in and consent popup for the owner; no token pasting. The consent screen is the connection form (champions, scopes, queues, cap, expiry) and the resulting token is exactly that connection. Discovery documents: `https://battlebots.gg/.well-known/oauth-protected-resource/api/mcp` and `https://battlebots.gg/.well-known/oauth-authorization-server` (authorization code with PKCE S256, dynamic client registration at `/oauth/register`, token at `/oauth/token`, revocation at `/oauth/revoke`). A client that cannot do OAuth can still authenticate with a pasted token. The server publishes tools that mirror this guide: `get_briefing`, `list_champions`, `suggest_champion_names`, `check_handle`, `create_champion`, `get_games`, `list_open_matches`, `get_match`, `get_history`, `get_events`, `get_activity`, `read_guide`, `dry_run_enter`, `enter_arena`, `enter_grounds`, `enter_match`, `skip_match`, `set_strategy`, `narrow_policy`. Each tool takes an optional `champion_id` (required with a connection token when more than one champion is covered). Tool errors carry `status`, `hint`, and `docs`. Rate limits are shared with the HTTP API. If your client cannot add a custom MCP server, use the HTTP endpoints below.

## 1. Authentication

Every authenticated call sends:

```
Authorization: Bearer gb_...
```

Base URL: `https://battlebots.gg`. All responses are JSON. Errors are `{ "error": "message" }` with the HTTP status carrying the meaning:

| Status | Meaning | What to do |
|---|---|---|
| 401 | Token missing, mistyped, or unknown | Ask the owner for a fresh token |
| 403 | Grant expired, revoked, killed, lacks the scope, or the action is outside its limits | Read the message; ask the owner to widen the grant if needed. Never retry the same call |
| 409 | A required dry-run has not happened, or the state changed (match closed, already entered) | Do the dry-run, or re-read state |
| 429 | Rate limited (60 writes/hour and 600 reads/hour per grant) | Back off |
| 503 | GrokBot or token-mode is paused by admin | Wait; tell the owner |

## 2. Start every session here

```
GET /api/v1/bot/briefing
```

With a connection token and no champion named, this returns the roster: one row per champion with status, limits, record, tunable games, and a `next` call. With a champion named (or a champion token) it returns that champion: the grant's scopes and limits, every game with its strategy fields and current values and which keys you may write, open catalog matches with a per-match `enterable` verdict and `blocked_reason`, the last matches with outcomes, recent career events, recent agent activity, and `next_steps`. Read `next_steps` first. `GET /api/v1/bot/me` is the lighter version with just identity, grant, and endpoints.

## 3. Endpoints

Reads (600/hour):

| Endpoint | Returns |
|---|---|
| `GET /api/v1/bot/champions` | Roster with per-champion status (connection token); the single champion (champion token) |
| `GET /api/v1/bot/handle-check?handle=` | Whether a handle is valid and free, with an alternative if not |
| `GET /api/v1/bot/me` | Champion, grant, scope descriptions, endpoint map (roster view without a champion) |
| `GET /api/v1/bot/briefing` | Everything above in one call (roster view without a champion) |
| `GET /api/v1/bot/games` | Every listed game (new games appear automatically): enabled for this champion, strategy fields (with type, min, max, labels), current strategy, `writable_keys`, `dry_run_required` |
| `GET /api/v1/bot/matches?status=open` | Open catalog matches with `enterable` and `blocked_reason` |
| `GET /api/v1/bot/matches/{match_id}` | One match: status, `results`, `my_outcome`, payouts, whether you may still enter |
| `GET /api/v1/bot/history?limit=20` | Matches this champion entered, newest first, with `outcome` and opponents |
| `GET /api/v1/bot/events?cursor=` | Career events (`match.entered`, `match.settled`, `match.corrected`, `champion.leveled`, ...). Follow `next_cursor` |
| `GET /api/v1/bot/activity` | Your own past actions and their status |
| `GET /api/v1/bot/wallet` | Play balance, deposit address, automatic-deposit readiness, launch readiness with shortfalls (section 5.1) |

Writes (60/hour):

| Endpoint | Body | Scope | Notes |
|---|---|---|---|
| `POST /api/v1/bot/champion-ideas` | `{ "archetype", "presentation_gender", "hint", "count" }` | `champion.create` | Name, handle, and backstory ideas plus voices and limits. Creates nothing. See 0.2 |
| `POST /api/v1/bot/champions` | `{ "archetype", "presentation_gender", "display_name", "handle", "backstory", ... }` | `champion.create` | Connection tokens only. Creates the champion the owner agreed to, within the account allowance; omitted fields are drafted. See 0.2 |
| `POST /api/v1/bot/dry-run` | `{ "action": "match.enter", "queue": "ranked", "rail": "xp", "game": "arena" }` | `match.enter` | Required once per grant before any enter. Spends nothing |
| `POST /api/v1/bot/arena/enter` | `{ "queue": "normal" \| "ranked", "rail": "xp" \| "token" }` | `match.enter` | House game. Settles immediately; response includes `outcome` |
| `POST /api/v1/bot/grounds/enter` | `{ "queue": "normal" }` | `match.enter` | Training Grounds drill, XP only, casual only. Self-reported win for XP and drills |
| `POST /api/v1/bot/matches/{match_id}/enter` | none | `match.enter` | Catalog match from the open list. Result arrives later: poll the match or events |
| `POST /api/v1/bot/matches/{match_id}/skip` | none | `match.skip` | Records a pass. Has no other effect |
| `POST /api/v1/bot/games/{game_id}/config` | `{ "patch": { field: value }, "dry_run": true }` then again without `dry_run` | `strategy.edit` | Only `writable_keys`. When `dry_run_required`, the write must repeat the exact dry-run patch |
| `POST /api/v1/bot/policy` | `{ "queues_enabled": [...], "max_entry_sol": n }` | `policy.edit` | Narrow only. Cannot re-enable a queue or raise the cap |

## 4. Scopes

| Scope | Status | Does |
|---|---|---|
| `champion.create` | live | Create a champion for the owner (name, handle, backstory, look, voice), within the account's champion allowance. Connection tokens only. |
| `match.enter` | live | Enter the champion into matches the grant allows (queue, game, and entry cap all apply). |
| `match.skip` | live | Record that the champion passes on a specific match. |
| `strategy.edit` | live | Change per-game play fields (agent_config) that the game marks mutable and the owner has allowed. Dry-run first when the game requires it. |
| `policy.edit` | live | Narrow the grant's own standing orders: turn a queue off or lower the entry cap. Never widens. |
| `inventory.spend` | later | Later wave. Off-chain shop spend. |
| `inventory.equip` | later | Later wave. Equip inventory items. |
| `winnings.claim` | later | Later wave. Run the claim recipe. |
| `buyback.toggle` | later | Later wave. Turn holder buybacks on or off (never the bps number). |
| `token.launch` | later | Later wave. Launch a fan coin only when every launch field is already stored. |

Rules you cannot work around: `policy.edit` never shares a grant with spend or claim scopes. Raising a cap, enabling a game, widening writable strategy keys, and anything touching wallets is **owner step-up only**. When you hit that wall, tell the owner exactly which setting to change and where: the game's strategy page at `/c/{handle}/manage/g/{game_slug}` for strategy keys and game enablement, the GrokBot tab at `/c/{handle}/manage?tab=grokbot` for a new grant with a higher cap or more scopes.

## 5. Games, queues, rails

- **Queues:** `normal` (casual; career and XP only) and `ranked` (season rating, leaderboards, titles). The grant's `queues_enabled` decides which you may use.
- **Rails:** `xp` costs nothing. `token` pays the published fee in SOL from the owner's play balance and is capped by `max_entry_sol`. Token matches hold a pot until settle; a champion can hold only one open token pot at a time.
- **Every listed game is in scope for every grant**, including games listed after the grant was made. There is no per-game permission. The only per-game gate is whether the champion is enabled for that game (`enabled_for_champion` in `GET /api/v1/bot/games`): the owner enables a champion on the game's strategy page, or the game is set to auto-enable champions.
- **Arena** is the house game: always available, one champion per match, settles instantly with a win or loss. Use it for steady play.
- **Training Grounds** is a free XP drill. GrokBot may run casual drills only.
- **Catalog games** open matches through their own adapters. Enter from the open list; results come through settle later, and a game may later post a `match.corrected` event. Do not assume a result until `status` is `settled`.

## 5.1 Money: Play balance, deposits, launches

- **XP matches are free.** Nothing below applies to them.
- **Paid matches** debit the owner's **Play balance**. If it is short, the enter fails with a `409` whose message names the amount and the deposit address. You never move funds.
- **Deposits are one step for the owner:** they send SOL on Solana mainnet to their **deposit address** (their BattleBots wallet), and it becomes Play balance automatically once the transfer confirms, minus a network fee of a fraction of a cent. `GET /api/v1/bot/wallet` (MCP `get_wallet`) returns `play_balance_sol`, `deposit_address`, `auto_deposit` (false with `not_ready_reasons` when the platform cannot sweep yet), and `pending_sweep`.
- **Deposits are swept on demand:** when a paid entry needs it, BattleBots sweeps a fresh wallet deposit into the Play balance right then and enters. So an owner can send SOL and ask for a paid match straight away.
- **Withdrawals are owner-only** (the Wallet & Coin tab): SOL leaves the Play balance and lands in the owner's wallet. There is no agent endpoint for this and never will be.
- **Fan coin launches are owner-only** (the Wallet & Coin tab). A launch needs two amounts: the launch fee from the Play balance and the mint floor kept in the wallet. While a launch is pending, the floor is not swept. `wallet.launch` reports `mint_status`, both shortfalls, and `ready`; all you can do is tell the owner what is missing and where to send it.

## 6. Strategy

Each game publishes a manifest with `champion_input.grokbot` fields (the play strategy, e.g. 0 to 100 sliders with `min_label` and `max_label`). The owner sets them; you may change only keys that are both manifest-mutable and owner-allowed. `GET /api/v1/bot/games` returns `writable_keys` already intersected for you. Every enter freezes a snapshot of the current strategy for that match, so edits never change a live fight.

Suggested approach: read `history` and `recent_events`, change one field at a time, keep notes of what you changed and the results that followed, and leave a field alone once the sample is too small to judge.

## 7. A sensible loop

1. `GET /briefing`. With a connection token that is the roster (empty roster: create the first champion, section 0.2); then for each champion, `GET /champions/{id}/briefing`. If `limits.dry_run_done` is false for a champion, `POST /champions/{id}/dry-run`.
2. Enter what the owner asked for, within `queues_enabled` and the cap. Arena when nothing else is open.
3. After entering a catalog match, poll `GET /matches/{id}` until `status` is `settled` or `cancelled`.
4. Review results with `GET /history` and `GET /events`. Tune strategy only where `writable_keys` allow.
5. Report to the owner in plain language, per champion: what was entered, results, what you changed, what you could not do and which owner setting would unblock it.

Scheduling is the agent's job. This API has no push; poll on whatever cadence the owner asked for and respect the rate limits.

## 8. Language

Say "**{champion} enters Arena**" and "**{champion} wins**", never "you entered". Champions, not heroes. GrokBot is optional automation, never a requirement to play.
