Cr Review
Review GitBook change requests from Claude Code by calling the GitBook REST API directly with curl (no CLI) — the reviewer-side companion to cr-create (the authoring side over the same API). Discover the change requests that need review (filter by who opened them, by space, or across a whole org), get the GitBook app link to review the diff, summarize what actually changed in a CR, then leave comments and optionally submit a review verdict (approve / request changes). Use this whenever someone wants to review docs change requests over the raw API (curl/HTTP), asks "what CRs are open / waiting on me / opened by <person>", "show me the change requests in <space>/<org>", "summarize what changed in this CR", "review this change request", "leave a comment on a CR", or "approve / request changes on a CR". For the authoring side (create a CR, push content, request reviewers, fix comments) over the API, use cr-create instead.
- Skill ID
- gitbookio/gitbook-skills/cr-review
- Publisher
- gitbookio
- Repository
- gitbook-skills
- Installs
- 0
- Files
- 1
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
gitbookio/gitbook-skills/cr-reviewInstalls these files- SKILL.md
What this skill tells the agent
GitBook CR Review (direct API)
Review documentation change requests against a GitBook space or org entirely through the GitBook REST API (https://api.gitbook.com/v1, hit with curl), so a reviewer never has to leave Claude Code to find what needs review, understand what changed, and respond. This is the reviewer-side companion to cr-create (the authoring side over the same API). The reviewer flow is: discover → understand → comment → decide.
Because every step is a real HTTP call, never fake an output: if a call returns nothing, says nothing changed, or errors, report exactly that.
Auth and the gbapi helper
Every call is a Bearer-authenticated request to https://api.gitbook.com/v1. The token lives in `GITBOOK_TOKEN` in the repo-root .env (create one at https://app.gitbook.com/account/developer). Never print the token; never write it to a tracked file. Define this helper once per session and use it for every call below — it fails loudly on any non-2xx and prints the API's error body (curl --fail-with-body, curl ≥ 7.76 / stock on current macOS):
set -a; [ -f .env ] && . ./.env; set +a # load GITBOOK_TOKEN
gbapi() { # gbapi METHOD /path [extra curl args…]
local method="$1" apipath="$2"; shift 2 # NB: not `path` — in zsh that is tied to $PATH
curl -sS --fail-with-body -X "$method" \
"https://api.gitbook.com/v1${apipath}" \
-H "Authorization: Bearer ${GITBOOK_TOKEN}" \
-H "Content-Type: application/json" "$@"
}Every response is JSON — pipe it through jq and read whole objects. Never hand-parse by grepping/line-pairing fields — bind the wrong title↔id and every downstream call runs against the wrong space/CR (a confident "0 comments" from a space that isn't the one you meant). If gbapi exits non-zero, surface the printed error — do not report success.
Endpoint map (verified against api.gitbook.com/openapi.json)
<org>, <space>, <cr>, <pageId> are the relevant IDs. Base URL is https://api.gitbook.com/v1; paths are relative to it.
| Step | Method + path | Notes | |
|---|---|---|---|
| Who am I | GET /user | your own user ID is .id (for requestedReviewer=me) | |
| Resolve a person → user ID | `GET /orgs/<org>/members?search=<name\ | email>` | match on user.displayName/user.email; the user ID is id (= user.id) |
| List orgs (to get IDs) | GET /orgs?limit=100 | .items[] → id, title | |
| List spaces in an org | GET /orgs/<org>/spaces?limit=100 | .items[] → id, title | |
| Discover CRs across an org | GET /orgs/<org>/change-requests?[status=][&creator=][&space=][&site=][&requestedReviewer=][&contributor=][&orderBy=] | ||
| Discover CRs in a single space | GET /spaces/<space>/change-requests?[status=][&creator=][&requestedReviewer=] | `status` is effectively required — omitting it returns an empty list, not everything | |
| CR detail | GET /spaces/<space>/change-requests/<cr> | subject, status, createdBy, comments, urls.app | |
| Link to review the diff | use .urls.app straight from the list/get output — never construct a URL | ||
| Link to the rendered preview | GET /spaces/<space> → .organization, then find the site behind the space, read its urls.published/urls.preview, and append `/~/changes/<number>/` | urls.app is only the diff view — see "Surfacing the preview link" in the cr-create skill for the full resolution steps. A bare site URL is not a preview of the CR: without the ~/changes/ segment it renders the site's current content. Reviewers deciding approve/request-changes usually want the rendered result, not just the diff | |
| Structural change summary | GET /spaces/<space>/change-requests/<cr>/changes | entries like page_created/page_edited with page.title, page.path. Payload is `{changes, more}` — read `.changes`, not `.items` | |
| Per-page prose diff | CR side GET /spaces/<space>/change-requests/<cr>/content/page/<pageId>?format=markdown vs base GET /spaces/<space>/content/page/<pageId>?format=markdown, diffed client-side | input to a prose summary only — never paste this as a line-by-line diff; point the user at urls.app for the actual diff | |
| Existing comments (context) | GET /spaces/<space>/change-requests/<cr>/comments?format=markdown&status=all | bodies at body.markdown; poster at postedBy.id; classify human vs gitbook:agent | |
| Leave a comment (GATE) | POST /spaces/<space>/change-requests/<cr>/comments body {"body":{"markdown":"…"}} (opt. "page"/"node") | posts publicly, notifies the author | |
| Submit a verdict (GATE) | POST /spaces/<space>/change-requests/<cr>/reviews body `{"status":"approved"\ | "changes-requested"} (opt. "comment":{"markdown":"…"}`) | records a real review |
| Existing reviews / your own | GET /spaces/<space>/change-requests/<cr>/reviews |
status on a review submission accepts exactly `approved` or `changes-requested` (verified against the API enum ChangeRequestReviewStatus). This skill does not merge a CR (POST …/merge) — merging changes shared state and is out of scope here.
CR-list filters and the authors note
- The CR-list filters (
status,creator,space,site,requestedReviewer,contributor,orderBy) are scalar query params and work directly.statustakes a single value (draft/open/archived/merged) — for "any state," union client-side. Omittingstatusreturns an empty list rather than everything, so always pass one. Default triage discovery tostatus=open, but never filter toopenwhen the user asks for the "latest" or "most recent" CR — a space's newest CR is often a draft. - The comments
authorsfilter does work over the raw API (…/comments?authors=<id>, repeatable). Even so, to split human vs agent you pull all comments and classify onpostedBy.id(a filter narrows, it doesn't classify).
API behaviors to watch
- Every endpoint returns JSON.
GET /useryields your.iddirectly — pipe every response throughjq. - The `authors` server-side filter is available (see above).
- Pagination is invisible. List responses return a capped page with no total or next-cursor. Raise
limit, and paginate with the response'snext.pagecursor passed aspage=— it is not an integer offset, sopage=1returns HTTP 400. Do this before concluding "not found."
Prerequisites
- `curl` and `jq` on your
PATH, and network access toapi.gitbook.com. - `GITBOOK_TOKEN` in the repo-root
.env(see "Auth"). Confirm withgbapi GET /userbefore running actions. - The scope IDs you want to review: an org ID (org-wide discovery), a space ID (single space), and the CR ID once chosen.
GET /orgsandGET /orgs/<org>/spacesgive IDs. - To filter by a person you need their user ID —
creator/requestedReviewertake IDs, not names. Resolve a name/email withGET /orgs/<org>/members?search=…first.
Hard rules
- Never invent IDs, URLs, CR subjects, change summaries, comment text, or "success." Run the call and report exactly what the API returns. If
gbapierrors, surface the error body. The diff link must be the API'surls.app, not a hand-built URL. - Always prefer GitBook's own diff over a hand-built one.
urls.appopens the diff GitBook itself renders (word-level, syntax-aware, split-view where the org has it enabled) — treat it as the diff of record for the CR. The per-page markdown fetch-and-compare in "Summarizing a CR" exists only to inform a prose summary of what changed — never paste a raw unified / line-by-line diff into chat as a substitute for it. - Surface the site preview link alongside the diff link, not just
urls.app— it lives on theSiteobject (urls.preview), not on the change request, so it's easy to forget it
More from gitbookio
Use this skill in a project
Open any RiverX project, search the Skills panel for gitbookio/gitbook-skills/cr-review, and install it in one click.
