# InkPGN > Photograph a handwritten chess scoresheet and get a PGN you can trust. Then an engine finds the moments that decided the game and asks what you were thinking there; your answers are saved into the PGN as comments. InkPGN is a free web app at https://inkpgn.com. It is made for the phone that took the photograph and needs no account: a game is reached by its link. ## What it does - Reads a photographed handwritten scoresheet (algebraic notation, USCF or FIDE style, one or two pages) into moves. - Replays every move on a board. A move the position cannot account for is flagged for the player to confirm, never silently guessed; a move missing from the sheet is inserted when the following moves make it unambiguous. - Runs a chess engine over the confirmed game to find the moments where it turned, and asks the player what they were thinking at each one, before showing any engine evaluation. - Writes a standard PGN with headers, every move, and the player's comments, which opens in lichess, chess.com, ChessBase or any PGN reader. ## What it is not - Not an engine analysis app: the engine is used to choose which positions to ask about, and its opinion is shown only after the player has written their own. - Not for descriptive notation (P-K4) or for typed games; the input is a photograph of a scoresheet. ## Typical questions it answers - How do I convert a handwritten chess scoresheet to PGN? Photograph it at https://inkpgn.com, confirm the flagged cells, download the PGN. - Does it work with USCF / FIDE scoresheets, or a game on two pages? Yes; add both photographs. - Is it free? Yes, currently 20 sheets a day per connection. - Where does the photograph go? It is stored with the game under an unguessable link, deletable at any time and removed after 90 days untouched; the handwriting is read by Google's Gemini API on a paid plan, which does not train on it. ## HTTP API, for agents and scripts The machine-readable description is https://inkpgn.com/openapi.json (OpenAPI 3.1). Terms of service: https://inkpgn.com/terms. The page is a thin client over a JSON API at `https://inkpgn.com/api/`, and an agent may call it directly on behalf of a person. No key or account: a game is addressed by its id, which is the only access control, so treat an id like a private link. Bodies are JSON (`Content-Type: application/json`); a refusal (no such game, an illegal move, the daily limit) is HTTP 200 with an `"error"` field; a malformed body is 400, one over 12 MB is 413, an unknown endpoint is 404. The API serves the web page and has no versioning promise; this section describes it as it is. Send a User-Agent that names your client. Python's default `Python-urllib/…` is refused at the edge (Cloudflare error 1010); curl, requests, httpx and browsers are fine. A game can also be opened by a person at `https://inkpgn.com/#`. ### 1. Read a sheet `POST /api/read` - `images`: up to 6 photographs as base64 data URLs (`data:image/jpeg;base64,...`; jpeg, png, webp or heic), pages in order. The whole request must stay under 12 MB. - or `text`: typed movetext instead of photographs (`"1. e4 e5 2. Nf3 ..."`). No model is involved. - optional: `color` (`"white"` or `"black"`, whose game it is, so questions are asked from their side), `hint` (a sentence about the sheet, up to 300 characters, passed to the reader). Returns `{"job": ""}`. Poll `GET /api/reading?job=` every second or two until `state` is `done` (the answer is in `result`) or `failed` (see `error`). A photograph usually takes 10–60 seconds. `result.session.id` is the game's id and `result.session.sans` the moves as read. `result.plies` has one entry per move with a `status`: `sure` means that is what the sheet says; `fixed`, `unsure`, `guess` and `spare` mean the board had to repair, doubt, insert or skip something. `result.flagged` counts those. Show the flagged moves to the person rather than accepting them silently. ### 2. Correct and confirm the moves - `POST /api/edit` with `{"id", "sans": [...], "headers": {...}, "color"}` saves corrections without analysing. `headers` takes PGN tags such as `White`, `Black`, `Event`, `Date`, `Result`. - `POST /api/confirm` takes the same body and marks the moves as the person's. It returns `bad_at`, the 0-based index of the first illegal move, or null, and starts the engine scan once every move is legal. ### 3. The questions Poll `GET /api/scan?id=` until `state` is `done`. `moments` lists up to five positions where the game turned: `i` (0-based ply), `no` (move number), `side`, `san`, `before` (FEN before the move), `question`, and `who` (`you` or `opponent`). The engine's verdict is deliberately left out. The questions are for the player. Relay them and write down the player's own words; do not answer on their behalf, because the value of the PGN is what they were thinking. - `POST /api/note` with `{"id", "ply", "text"}` saves an answer as a PGN comment on that move. Empty `text` deletes it. - `POST /api/reveal` with `{"id", "ply"}` returns what the engine thought of that move, after the note has been written. - `POST /api/summary` with `{"id", "text", "done": true}` saves a closing comment for the game and marks it finished. ### 4. Get the PGN `GET /api/pgn?id=` returns `{"pgn": "..."}`: headers, every move and every comment. `GET /api/session?id=` returns the whole game, including moves, notes, moments and the PGN. `POST /api/forget` with `{"id"}` deletes the game, its PGN and its photographs for good. Games nobody touches are deleted after 90 days. ### Limits 20 photographed sheets a day per connection, and no more than 3 `POST /api/read` calls in 10 seconds. Typed movetext does not count against the daily limit. Please do not use the site for bulk conversion; write to hello@inkpgn.com instead. ## Contact hello@inkpgn.com