gitbookio/gitbook-skills3 files

Cr Create

Drive an end-to-end GitBook docs review flow from Claude Code by calling the GitBook REST API directly with curl (no CLI) — create a change request, push content (update an existing page AND create a new page), request reviewers, notify Slack, then pull review comments back in, fix them, re-push, and resolve. This is the authoring-side companion to cr-review (the reviewer side over the same API). Use this whenever someone wants to run a "docs review in GitBook" loop from the terminal/agent against the raw API (curl/HTTP), mentions creating a change request via the API, pushing content into a CR, "pull in the latest comments and fix them," requesting review on docs, or showing engineers how to collaborate on GitBook docs from Claude + Slack without a CLI.

Specification
Skill ID
gitbookio/gitbook-skills/cr-create
Publisher
gitbookio
Repository
gitbook-skills
Installs
0
Files
3
Synced
Sep 23, 2026
How to use it

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-createInstalls these files
  • SKILL.md
  • references/env.example
  • references/gitbook-review.config.json

What this skill tells the agent

GitBook Review Flow (direct API)

Run a documentation review loop against a GitBook space entirely through the GitBook REST API (https://api.gitbook.com/v1, hit with curl), so an engineer never has to leave Claude Code (plus Slack) to propose docs changes and get them reviewed. This is the authoring-side companion to cr-review (the reviewer side over the same API). Every action here is a plain HTTP call — there is no CLI and no helper script.

The same actions serve three purposes with no separate code paths:

  • CR-creation demo — create a change request and push content (one existing page updated, one new page created).
  • Notify/review demo — request reviewers, drop a Slack link, pull comments, fix, re-push, resolve.
  • Real use — the identical actions against the user's own content.

Because the demo is just a scripted sequence of the real actions, it cannot show something that doesn't actually work. Keep it that way: never fake an output.

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. If it's missing, prompt the user for it and write it to .env; don't invent one.

Define this shell helper once per session and use it for every call below. It loads the token from .env, sets the base URL and headers, and — critically for the "never fake output" rule — fails loudly on any non-2xx, printing 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 (and SLACK_WEBHOOK_URL)
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 you act on the wrong space/CR). If gbapi exits non-zero, surface the printed error — do not report success.

Endpoint map (verified against api.gitbook.com/openapi.json)

<space>, <cr>, <pageId>, <commentId> are the relevant IDs. Base URL is https://api.gitbook.com/v1; all paths below are relative to it.

StepMethod + pathNotes
Who am IGET /userreturns {id, displayName, email} — your own user ID is .id
List pagesGET /spaces/<space>/content/pagesflat-ish tree with id, title, type
Get a page (base)GET /spaces/<space>/content/page/<pageId>?format=markdowncurrent markdown of a page on the live space
Create CR (GATE)POST /spaces/<space>/change-requests body {"subject":"…"}returns the CR object with id and urls.app (also a Location header) — `urls.app` is only the editor/diff link, not a rendered preview; see "Surfacing the preview link"
Get CRGET /spaces/<space>/change-requests/<cr>subject, status, createdBy, comments, urls.app
Push contentPOST /spaces/<space>/change-requests/<cr>/content body {"changes":[…]}1–50 ops, applied sequentially in one new revision; all-or-nothing
Find the site behind a spaceGET /spaces/<space> → .organization; GET /orgs/<org>/sites; GET /orgs/<org>/sites/<site>/site-spaces → match .items[].space.idneeded only to resolve the site preview link (see below); a space isn't required to belong to a site
Get a site (for its preview link)GET /orgs/<org>/sites/<site>urls.preview (draft/CR content), urls.published (only once live) — not part of the change-request response at all
Get a page (CR side)GET /spaces/<space>/change-requests/<cr>/content/page/<pageId>?format=markdownverify what actually landed in the CR
Request reviewers (GATE)POST /spaces/<space>/change-requests/<cr>/requested-reviewers body {"users":["…"]}array of user IDs; optional subject/description
List commentsGET /spaces/<space>/change-requests/<cr>/comments?format=markdown&status=allbodies at body.markdown; location under target.page/target.node; poster at postedBy.id
Reply to a commentPOST /spaces/<space>/change-requests/<cr>/comments/<commentId>/replies body {"body":{"markdown":"…"}}
Resolve a comment (GATE)PUT /spaces/<space>/change-requests/<cr>/comments/<commentId> body {"resolved":true}resolves unconditionally — no reply-first guard (enforce it yourself)
Reply list (verify)GET /spaces/<space>/change-requests/<cr>/comments/<commentId>/repliesconfirm a reply exists before resolving

Not a GitBook API operation: any Slack/Channels action. Slack is sent separately (see "Slack is a stopgap").

Content-change ops (the changes array)

Each item in changes is discriminated by operation:

  • `update_page` — {"operation":"update_page","page":"<pageId>","document":{"markdown":"…"}}. REPLACES the whole page document. document accepts only {"markdown":"…"} — not the node tree that GET …/page returns with format=document (pushing that 422s). It cannot rename a page (there is no title/slug field). Fetch the current markdown, edit it, push it back — or you drop existing blocks.
  • `insert_page` — {"operation":"insert_page","title":"…","document":{"markdown":"…"}}. into (parent page ID) is optional — omit it to insert at the space root; at (index) is also optional. title is required (only insert_page sets a title, at creation).
  • `delete_page` — {"operation":"delete_page","page":"<pageId>"}. This flow never deletes; documented for completeness.

The markdown round-trip is LOSSY — see "Editing an existing page safely" before you re-push an edited page.

API behaviors to watch

  • Every endpoint returns JSON. GET /user is just JSON with an .id — pipe every response through jq.
  • The `authors` comment filter works server-side. GET …/comments?authors=<id> is a real array query param (repeat authors= for several). You still pull all comments and split human vs agent on postedBy.id (see "Two operations") — a filter narrows, it doesn't classify — but the server-side filter is available if you want it.
  • Nothing normalizes content for you. The API does not strip the duplicated leading H1 or collapse multi-line {% … %} blocks before sending. You must do those transforms yourself before every push (see "Editing an existing page safely"). This is the easiest thing to get wrong — don't skip it.

Surfacing the preview link (do this every time)

This rule is transport-agnostic — it applies whether the change request was pushed via this skill's `curl` calls, or via `configure-site`/`write-docs` over the GitBook MCP server. The underlying gap is the same in both cases: nothing in the change-request response points at the rendered preview, so it's easy to file this under "REST-only demo detail" and skip it when the push actually happened over MCP. It isn't optional in either case. If you reach this skill's docs while working from configure-site or write-docs, translate the REST calls below to their MCP equivalents (getSpaceById, list_sites/get_site_structure, getSiteById via invoke_operation) rather than skipping the step because the transport doesn't match.

A change request's own response only ever gives you urls.app — the link to the editor / diff view in the GitBook app. It is easy to stop there and assume that's "the link" for the

More from gitbookio

Use this skill in a project

Open any RiverX project, search the Skills panel for gitbookio/gitbook-skills/cr-create, and install it in one click.

Use this skill