# API & MCP reference — artifacts.md

Drive artifacts.md headlessly over REST or a live MCP server. Authenticate an
agent, create and update tickets in one idempotent call, and drive a board from
any MCP-aware client — the same bearer key, the same core, the same objects.

Base URL: `https://app.artifacts.md`

All commands below are verified against the live API.

## Authentication

Every request authenticates with a board-scoped bearer key in the
`authorization` header. Get one — with a live board — in a single call, no human:

```
curl -fsS -X POST https://app.artifacts.md/agent/identity \
  -H 'content-type: application/json' -d '{"type":"anonymous"}'
```

Response (abridged):

```
{
  "credential": { "api_key": "tix_sbx_…", "token_type": "Bearer" },
  "board": { "slug": "eee300e0", "url": "https://artifacts.md/eee300e0" },
  "pre_claim_scopes": ["boards:read","boards:write","tickets:read","tickets:write"],
  "claim_token": "clm_…",
  "claim_url": "https://artifacts.md/claim/JDE4-W8KJ",
  "expires_in": 1209600
}
```

- `credential.api_key` — use immediately; scoped to this ONE board.
- `board.url` — give to a human to watch the board update live.
- `claim_token` — keep private; a human uses `claim_url` to keep the board.

Pre-claim scopes are a hard ceiling: read/write on boards + tickets for that one
board, nothing else — no attachments, no admin, no other boards.

```
export TIX_API_KEY=tix_sbx_…
SLUG=eee300e0
```

## REST endpoints

### Create a ticket

```
curl -fsS -X POST https://app.artifacts.md/v1/boards/$SLUG/tickets \
  -H "authorization: Bearer $TIX_API_KEY" \
  -H 'content-type: application/json' \
  -d '{"title":"Ship the landing page","column":"To review"}'
```

Returns the created ticket, including a stable `key` (e.g. `EEE300E0-1`), its
`number`, `columnId`, `status`, and `docVersion`.

### List tickets

```
curl -fsS https://app.artifacts.md/v1/boards/$SLUG/tickets \
  -H "authorization: Bearer $TIX_API_KEY"
```

### Batch-create (one atomic request)

Per the Convex write discipline, bulk work is one atomic mutation — never a
per-ticket loop that fights on a hot document:

```
curl -fsS -X POST https://app.artifacts.md/v1/boards/$SLUG/tickets/batch \
  -H "authorization: Bearer $TIX_API_KEY" -H 'content-type: application/json' \
  -d '{"items":[{"title":"First"},{"title":"Second"}]}'
```

## The CLI / API contract

- **Stateless & idempotent.** Explicit/absolute refs, one call = one durable
  update, safe to retry (`--idempotency-key`), safe to batch and parallelize.
- **Slim & token-efficient.** Returns the minimum by default; `--verbose` to
  expand. Least-privilege, scoped tokens.
- **Machine-first.** `--format json|ndjson|table`, stable exit codes
  (2 usage / 3 ref / 4 cred), NDJSON stdin batch.

## MCP

There is a live MCP server at `POST https://app.artifacts.md/mcp` — Streamable
HTTP, JSON-RPC 2.0, authenticated with the same bearer key as REST and the CLI.
It exposes a fixed set of 11 verb tools (not a list generated per board):
`list_boards`, `create_board`, `board_schema_from_prompt`, `get_board`,
`create_ticket`, `list_tickets`, `ready_work`, `get_ticket`, `update_ticket`,
`move_ticket`, `claim_ticket`. The board's schema shapes each tool's arguments
and results (e.g. the field keys `create_ticket` accepts) — it does not generate
the tool list. Every tool re-issues through the same `/v1` REST core, so MCP ≡
CLI ≡ REST with no drift.

Point any MCP client at the endpoint; the official inspector lists all 11 tools:

```
npx @modelcontextprotocol/inspector --cli \
  https://app.artifacts.md/mcp --transport http --method tools/list
```

Machine discovery of the protected resource:

```
curl -fsS https://app.artifacts.md/.well-known/oauth-protected-resource
```

## Discovery documents

- `https://app.artifacts.md/AGENTS.md` — the full agent front door
- `https://app.artifacts.md/auth.md` — agent auth + claim protocol
- `https://app.artifacts.md/.well-known/oauth-protected-resource` — RFC 9728
- `https://app.artifacts.md/.well-known/oauth-authorization-server` — RFC 8414