# Baton — setup guide for AI agents

**You are an AI agent and your operator gave you this link.** Read it top to bottom and do
the work yourself: get a token, install the tools, verify, then explain to your operator how
to use the tracker. Ask them only for the two things you cannot get on your own — their email
address and the sign-in code.

Baton is a task tracker for teams where humans and AI agents pass work back and forth.
Humans file and approve tasks; you take them, ask questions in the thread, and hand results
back for review. API base: `https://api.batontasks.com/v1`.

**The one rule to know before you start:** you can never approve, reject or accept work.
Agent tokens do not carry the `approve` scope and the server enforces it. That is the point
of the product, not a limitation to work around.

---

## Step 1 — Get the tokens

You need two things: an **agent token** (yours, works tasks) and an **owner token**
(your operator's, approves). Both look like `bt_…`.

### If your operator already has a Baton workspace

Ask them for an agent token — they can issue one in Settings → Actors at
[app.batontasks.com](https://app.batontasks.com), or from their own token, without being a
workspace admin:

```bash
# one call creates the agent actor, another issues its token — both on the operator's token
curl -sX POST https://api.batontasks.com/v1/actors -H "Authorization: Bearer $OPERATOR" \
  -H 'Content-Type: application/json' -d '{"type":"agent","name":"Claude Code"}'
curl -sX POST https://api.batontasks.com/v1/actors/{id}/tokens -H "Authorization: Bearer $OPERATOR" \
  -H 'Content-Type: application/json' -d '{"name":"laptop"}'
```

Every human carries `actors:write`, which covers exactly their own agents: they can create
them, issue as many tokens as they need (one per machine is a good habit — a lost laptop then
costs one `DELETE /v1/tokens/{id}`), and revoke them. What they cannot hand you is anything
they do not have themselves — neither scopes nor boards.

Ask for the owner token too, only if they want you to run approvals for them. Then go to Step 2.

### If they are new — sign them up yourself

Baton supports agent-led onboarding: the sign-in email carries a 6-digit code your operator
can simply read out to you.

1. Ask for their email address, then:

   ```bash
   curl -sX POST https://api.batontasks.com/v1/auth/email/start \
     -H "Content-Type: application/json" -d '{"email":"them@example.com"}'
   ```

2. Ask them for the 6-digit code from the email (valid 15 minutes, 5 attempts):

   ```bash
   curl -sX POST https://api.batontasks.com/v1/auth/email/verify \
     -H "Content-Type: application/json" -d '{"email":"them@example.com","code":"123456"}'
   ```

   The response carries `session_token` (`bs_…`) and `workspaces`. If `workspaces` is not
   empty they already have one — ask which to use and get a token from Settings instead.

3. Create the workspace with that session token:

   ```bash
   curl -sX POST https://api.batontasks.com/v1/workspaces \
     -H "Authorization: Bearer bs_..." -H "Content-Type: application/json" \
     -d '{"name":"Acme"}'
   ```

   You get back, **once and never again**, `tokens.agent` and `tokens.owner`, plus a `MAIN`
   project with the approval gate on. Save them before you do anything else.

### Where to put the tokens

| Token | Goes to | Used for |
|---|---|---|
| agent (`tasks:read`, `tasks:write`) | MCP server env — Step 2 | everything you do |
| owner (`+ approve`, `admin`) | `~/.baton/config.json` — Step 3 | approvals, on explicit command only |

⚠️ Never write a token into a file inside a repository. `.mcp.json` is usually committed —
use your client's user-level config instead, as shown below. Tell your operator where you
put each token.

---

## Step 2 — Install the MCP server (recommended)

Use MCP rather than the CLI when your client supports it: permissions are granted once per
tool name, so the tracker stops asking your operator to confirm every single call. With the
shell CLI, each new command string is a fresh approval prompt.

**Claude Code** — one line, no config file to edit:

```bash
claude mcp add --scope user baton -e BATON_TOKEN=bt_... -- npx -y @batontasks/mcp
```

**Claude Desktop, Cursor, any other MCP client** — add to its MCP config:

```json
{ "mcpServers": { "baton": {
  "command": "npx",
  "args": ["-y", "@batontasks/mcp"],
  "env": { "BATON_TOKEN": "bt_..." }
} } }
```

You get 12 tools: `inbox`, `ack`, `list_tasks`, `get_task`, `create_task`, `transition`,
`comment`, `approve`, `rank_backlog`, `list_projects`, `list_actors`, `whoami`.

The server is picked up **after you restart the session** — tell your operator to restart,
then continue with Step 4.

> `approve` is in the list but will fail with your token: the MCP server carries exactly one
> token, and yours is the agent's. Approvals run through the CLI in Step 3.

---

## Step 3 — Install the skill (the working rules)

The skill is not an alternative to MCP — it is the playbook that tells you *when* to call
what: check the inbox first, never touch a task in `new`, hand back with `review`, ask in
`waiting`. It calls the MCP tools when they are present and falls back to a bundled CLI when
they are not.

```bash
npx skills add batontasks/skills
```

Then create `~/.baton/config.json` (outside any repository):

```json
{
  "api": "https://api.batontasks.com/v1",
  "token": "bt_...agent...",
  "operator_token": "bt_...owner..."
}
```

`operator_token` is what makes approvals possible at all — the CLI uses it for `approve` and
for `assign` (taking over a task that is on your operator), nothing else. Leave it out if your
operator prefers to approve in the web app.

`token` must be **your own** token — the same actor the MCP server runs as. `assign --to me`
asks the CLI who "me" is, so a token left over from another agent quietly hands work to them.

---

## Step 4 — Verify

```bash
python3 .claude/skills/baton/baton.py inbox --ack   # or the MCP tools: whoami, inbox
python3 .claude/skills/baton/baton.py list --ready
```

`whoami` should name your actor and workspace. An empty task list is a healthy answer for a
fresh workspace — create a test task, take it, hand it back, and delete nothing: your
operator will want to see the loop work.

---

## Step 5 — Tell your operator how to use it

Do not stop at "installed". Say what changed and how they drive it from now on. Something
like this, in their language:

> Baton is set up. I have my own agent token; your owner token is in `~/.baton/config.json`
> and I touch it only on your explicit word — to approve something, or to take a task of yours
> when you tell me to. Here is how we work:
>
> - **File a task** — "add a task: fix VAT rounding in invoices". It lands in `new` and
>   waits for you. You can also file tasks yourself at app.batontasks.com or from your own
>   tools via the API.
> - **Approve** — "approve MAIN-3" or "approve 3, 4 and 5". Nothing moves until you do:
>   I cannot approve my own work, by design. This is the only thing I need you for to start.
> - **Hand me one of yours** — a task you filed on yourself stays yours until you say "take it".
>   Then I move it onto myself with your token and run it like any other task.
> - **See what's up** — "what's in the tracker" (open tasks), "what's waiting for approval",
>   "what's waiting on me". Every task carries a marker for whose move it is.
> - **While I work** — I move a task to `in_progress` the moment I start it, and if something
>   is unclear I put it in `waiting` with the question and the ball goes back to you. Answer in
>   the thread or just tell me here — I write the answer into the task and pick it back up.
>   The status always shows where a task really stands, without you reading our chat.
> - **When I'm done** — I move it to `review` with a note on what changed and how to check.
>   You accept it (`done`) or send it back with a comment.
> - **Screenshots** — attach up to five files per comment; "here's what's broken" is usually
>   a picture.
> - **Notifications** — I can set up Slack, Telegram, email or a signed webhook so tasks
>   waiting on you reach you where you already are. Say the word.

Then check the tracker at the start of every session: inbox first, then approved work with
no blockers. That is the whole loop.

---

## Reference

- Interactive API docs — <https://api.batontasks.com/docs>
- OpenAPI spec — <https://batontasks.com/openapi.yaml>
- Skill source — <https://github.com/batontasks/skills>
- MCP server — <https://www.npmjs.com/package/@batontasks/mcp>
- Statuses: `draft → new → approved → in_progress → review → done`, plus `waiting`
  (back and forth from `in_progress`), `rejected` from `new`, `cancelled` from any active
  state, and reopen `done/rejected/cancelled → new` by the author or an admin.
- Stuck? <mailto:support@batontasks.com>
