HTTP API
The UI is a client of a small HTTP API on the same port, and an agent can use it too. Prefer it to editing files
while the server runs: its writes are atomic and are merged with concurrent edits
(how). It answers on http://localhost:5622 only, and only to localhost
requests; writes must be JSON, except an image upload, whose body is the image (see
Localhost security).
P=my-feature; R=my-service; SHA=0a1b2c3d # a short or full shaBASE=http://localhost:5622/api/projects/$P/repos/$R/commits/$SHAJ='Content-Type: application/json'
curl -s $BASE/review | jq '.threads[] | select(.resolved != true)' # open threadscurl -s -X POST -H "$J" $BASE/threads/<tid>/comments -d '{"author":"claude","body":"…"}' # replycurl -s -X PATCH -H "$J" $BASE/threads/<tid> -d '{"resolved":true}' # resolvecurl -s -X PUT -H "$J" $BASE/pr -d '{"title":"…","body":"…"}' # null clears a fieldcurl -s -H 'Content-Type: image/png' --data-binary @shot.png \ "http://localhost:5622/api/projects/$P/assets?alt=Screenshot" | jq -r .markdown # add an imageMore recipes are in the agent guide.
Endpoints
Section titled “Endpoints”… below is /api/projects/:p/repos/:r/commits/:sha, where :sha is a short or full sha.
| Method | Path | |
|---|---|---|
| GET | /api/projects | Projects: slug, title, repo names/count, commit count (commitCount, without follow-on stacks and merged PRs; followOnCount, mergedCount), archived, example, unresolved count, last modified; plus projectsDir, home (the user’s home directory, so a client can show paths as ~/…) and examples: present, removed (removed earlier, restorable) or none. |
| POST | /api/examples | {}: add the example projects (create, restore removed ones, leave existing ones). Returns {created, restored, existing, removed} (slugs); 201 if it created any, else 200; 409 if a project of yours has an example’s name. The git work doesn’t block the server; a second request while one is running gets the same result. |
| POST | /api/examples/remove | {}: remove them (rename each project.yaml to a .bak; nothing deleted). Returns the same shape; 409 while they’re still being added. |
| GET | /api/projects/:p | description, repos (range, errors, warnings), stacks (title, color, commit shas, warning, description, follow_on: true on follow-on stacks, weight), commits (+/−, position, stack index, PR title, unresolved counts, branch, github, status, progress, landed on landed PRs: the record plus diff, and tip/parent: what’s diffed). |
| PUT | /api/projects/:p/description | {description: string | null}: the project intro (null or blank removes it). |
| PUT | /api/projects/:p/repos/:r/stacks/:n/description | {description, title?}: stack n’s note (1-based in stacks:). A title that no longer matches is a 409. |
| POST | /api/projects/:p/assets | Upload an image: the body is the image itself, with its type as the Content-Type (image/png, image/jpeg, image/gif, image/webp or image/svg+xml), up to 5 MB. Optional ?alt=<text>. Returns {name, markdown, created} (201; 200 with created: false if the same image was already there). See Images. |
| GET | /api/projects/:p/assets/:name | An image, by its <hash>.<ext> name (anything else is a 404). Served with its type, X-Content-Type-Options: nosniff, Cross-Origin-Resource-Policy: same-origin and Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline', so an SVG opened directly can’t run script. |
| GET | … | Metadata (including branch, github), message body, files with old/new contents, stack (with position/count), project-wide prev/next, pr, review. |
| GET | …/review | The review: the file’s contents with threads placed on this commit (outdated, relocated line), plus file, target_file (absolute paths), matched_from, matched_by, carried_over, parse_error. |
| GET | …/pr | {title, body, title_source, body_source: 'commit'|'override', commit_subject, commit_body, branch, github, file, …} |
| PUT | …/pr | {title?, body?, branch?, github?, status?}. null clears a field; status is "draft", "ready" or null. See PR files. |
| POST | …/threads | New thread: {body, path?, side?, line?, start_line?, start_side?, author?}. Without path/side/line it’s a general thread. |
| POST | …/threads/:tid/comments | Reply: {body, author?}. |
| PATCH | …/threads/:tid | {resolved}. |
| PATCH / DELETE | …/threads/:tid/comments/:cid | Edit {body} / delete. Only the reviewer’s own comments: an agent’s are never edited or deleted by the UI. |
| PUT | …/viewed | {path, viewed}: tick or untick a file’s Viewed box. |
| GET | …/stack.svg, …/stack.png | The stack picture with this PR highlighted. See below. |
| GET | /api/projects/:p/stack.svg, …/stack.png | The same for the whole project, nothing highlighted (as on the homepage). |
| GET | /api/me | The reviewer: {id, name} (see LOCAL_REVIEW_USER). |
| GET | /api/health | {ok: true}. |
| GET | /api/events | Server-Sent Events, below. |
Commit fields
Section titled “Commit fields”Each commit in GET /api/projects/:p (and the commit endpoint) carries:
status:draft,ready,publishedormerged. See Readiness.progress:{threads, resolved, files, viewed}: conversations (threads with at least one comment, outdated ones included) and how many are resolved, files changed and how many are marked Viewed.branch:{name, status: 'ok'|'moved'|'missing'|'discovered'|'merged', points_at?, discovered: string[]} | null. See Branches.github:{number, url, branch?, base?, created?, stack?, stacked_on?} | null.urlis''if the file gives only a number.
Stack picture options
Section titled “Stack picture options”The SVG is 680px wide; the PNG is drawn at 2x (1360px).
| Query | |
|---|---|
?scope=project (default), repo, stack | What’s in the picture. Only on the per-commit endpoint. |
?scale=sqrt (default), linear | How the +/− bars scale. |
?title=0 | Drop the project heading and summary line. |
?download | Add Content-Disposition: attachment. |
Events
Section titled “Events”GET /api/events is a Server-Sent Events stream. The server watches ~/.local-review/projects/ and sends:
{"type": "review", "project": "my-feature", "repo": "my-service", "sha": "…"}{"type": "pr", "project": "my-feature", "repo": "my-service", "sha": "…"}{"type": "project", "project": "my-feature", "reason": "…"}{"type": "projects", "reason": "…"}The UI uses it to refresh, so edits to the files (by an agent or an editor) show up within about a second.
Errors
Section titled “Errors”Errors are JSON, {"error": "…"}, with a status code:
- 400: a malformed request: not JSON, a missing
body, astatusother thandraft/ready/null, an ambiguous short sha, an empty upload. - 403: a
Host(or, on a write, anOrigin) that isn’t localhost. See Localhost security. - 404: no such project, repo, thread or comment, or a sha that isn’t in the repo’s range (it may have been
rebased away: look it up again in
GET /api/projects/:p). - 409: the file isn’t valid YAML, so the server won’t overwrite it until it’s fixed by hand (nothing is lost); or
a stack note’s
titleguard no longer matches. - 413: an upload over 5 MB. It’s refused from its
Content-Length, or as soon as a streamed body passes the limit, before being stored. - 415: a write (other than
DELETE) withoutContent-Type: application/json; on an upload, aContent-Typethat isn’t one of the image types, or contents that don’t match it (the file says it's PNG but its contents are JPG).