# Chessfolio authentication

Chessfolio has two agent surfaces, and they authenticate differently.

**Personal data** (a user's own games, stats and ratings) is reached with a personal access
token (PAT). Every `/api/v1/me` endpoint and every personal MCP tool returns
only the token owner's own data. This is the part that needs a token.

**Public tools** need NO token and carry no personal scope: the tournament
tools (`/api/v1/tournaments/*`; MCP `get_tournament_state`,
`estimate_round1`, `estimate_pairings`) read public Chess-Results data, the
classic-games library (`/api/v1/library*`; MCP `list_library_games`,
`get_library_game`) serves curated published games, and the ECF rating
calculator (`/api/v1/ecf/rating-change`; MCP `calculate_ecf_rating_change`)
is pure arithmetic over caller-supplied numbers.

Personal tools are read-only except nine narrowly scoped mutations, each acting on one exact
game, study-collection row or coaching row owned by the token holder: `attach_pgn` (REST `POST
/api/v1/me/games/{id}`) attaches validated PGN text, `request_game_review` (REST `POST
/api/v1/me/games/{id}/review`) queues or waits for an engine review,
`upload_study_game` (REST `POST /api/v1/me/study`) saves one validated PGN into the
token holder's own study collection, `request_study_analysis` (REST `POST
/api/v1/me/study/{id}/review`) is the study collection's review equivalent,
`delete_study_game` (REST `DELETE /api/v1/me/study/{id}`) removes one study game and its
notes from that collection, `set_training_focus` (REST `POST /api/v1/me/coaching/focus`)
creates, retitles or resolves one training focus of at most three active,
`add_coaching_entry` (REST `POST /api/v1/me/coaching/entries`) appends one entry to an
append-only log, `save_position` (REST `POST /api/v1/me/coaching/positions`) saves one
position, and `remove_saved_position` (REST `DELETE /api/v1/me/coaching/positions/{id}`)
deletes one saved position, the only coaching row an agent can delete. None can alter
results, ratings or other metadata, none accepts a URL, a path or an uploaded file, and none
touches another user's data.

## Getting a token

(You only need a token for personal data; the public tools — tournament, library and ECF calculator — work without one.)

1. Sign in (human, via Clerk) at https://chessfolio.io/login
2. Go to Settings → API access
3. Create a token — it is shown once; store it securely. Revoke any time.

## Using it

- REST: `Authorization: Bearer cfp_…` against https://chessfolio.io/api/v1/*
  (OpenAPI: https://chessfolio.io/openapi.json)
- MCP: streamable HTTP at https://chessfolio.io/api/mcp with the same header
  (works with claude.ai custom connectors and Claude Code today)

## Properties

- Nine narrow writes: `attach_pgn` accepts one parseable game as PGN text, strips comments/variations, and stores parser-generated canonical PGN; `request_game_review` queues (or optionally waits up to 45s for) an engine review of a game that already has moves — an already-reviewed game is returned free; `upload_study_game` saves one validated PGN into the token holder's own study collection; `request_study_analysis` reviews a stored study game from the same shared allowance; `delete_study_game` removes one owned study game and its notes (the underlying analysed game record is untouched); and the four coaching writes act only on the token holder's own coaching rows: `set_training_focus` creates, retitles or resolves one focus and never deletes one (at most three active), `add_coaching_entry` appends to an append-only log with every game and position reference proved owned first, though it makes two kinds of edit to an existing row: flipping one owned, open assignment to completed or skipped when given that assignment's id, and, when a result names a saved position, stamping that position's last-quizzed time and appending to its quiz history, which is how a quiz is recorded, `save_position` saves one position copied from the owner's own review when a game is named (up to 500), and `remove_saved_position` deletes one saved position, the only coaching row an agent can delete
- Owner-scoped: a personal-data token can access only its creator's data
- Hashed at rest, revocable, rate-limited: 120 requests/minute per user, plus six tighter buckets, each its own and each shared across REST and MCP. PGN attachment 20/hour; newly queued reviews 20/hour, one allowance shared by `request_game_review` and `request_study_analysis`; study-collection uploads 20/hour; the two per-ply analysis reads 30/minute; the weakness-profile read 10/minute; the four coaching writes 60/hour in one bucket between them. All but the PGN one are enforced atomically and fail closed
- No OAuth yet — PATs first; OAuth will follow if third-party demand appears

## Public tools (no token)

The tournament tools are not the only unauthenticated surface — three tool
families need no token at all: the tournament tools (`get_tournament_state`,
`estimate_round1`, `estimate_pairings`), the classic-games library
(`list_library_games`, `get_library_game`), and the ECF rating calculator
(`calculate_ecf_rating_change`). None carries personal scope, and each is
rate-limited to 60 requests/minute in its own bucket, so a busy day on one
cannot lock out the others — REST calls are limited per caller IP; MCP calls
share one bucket per family, since the MCP protocol carries no client address
to key on.

The tournament tools alone read public Chess-Results data (source:
chess-results.com). Their Round-1 and next-round pairing outputs are
ESTIMATES, not official pairings, and snapshots may be stale; those honesty
rules travel in the payloads and tool descriptions.

Human documentation: https://chessfolio.io/developers
