Apply a batch of content changes to a change request
Applies an ordered array of content changes (up to 50) to the change request atomically, creating a single new revision. Changes are applied sequentially; if any single change is invalid the entire batch is rejected and no revision is created. Use this to create, update, rename, move, or delete pages within a change request before merging to live. This is the primary way to programmatically author content in a change request. Renaming a page — update_page can change a page's title and/or its slug (the path segment used to build its URL within its current parent). Both fields are independent: to rename only the title without changing the URL, set title and omit slug; to change the URL without changing the title, set slug and omit title. Moving a page — update_page can also change a page's parent and/or its position among siblings with into/at. Set into to the new parent's page ID, or null to move the page to the root of the tree; omit into to leave the parent unchanged. Set at to place the page at a specific zero-based index among its new siblings; omit it to leave the position unchanged (when the parent isn't changing) or append at the end (when it is). A page cannot be moved under itself or one of its own descendants. insert_page's into/at remain the way to place a new page at creation time. To put an image or attachment in a page, upload it with an insert_files change. Give the file a ref and a page inserted in the same batch can reference it as  or [File](./<ref>), which resolves to the file's ID at apply time regardless of operation order. Uploaded files are also listed as insertedFiles when compat=false, and a file already in the change request can be referenced directly as /files/<fileId>. Inline data: URIs in markdown are not ingested and large image blocks are dropped, so upload the bytes with insert_files instead. Publishing an OpenAPI spec — an OpenAPI spec belongs to the organization (see createOpenAPISpec) and appears in a space only through a page that renders it. Insert that page with an insert_computed_page change: { "operation": "insert_computed_page", "title": "API reference", "computed": { "type": "builtin:openapi", "props": { "models": true }, "dependencies": { "spec": { "ref": { "kind": "openapi", "spec": "<spec slug>" } } } } }. GitBook replaces that page, at its position, with the API reference: one page per tag (or per operation with "grouping": "by-operation" in props), plus a models page when models is true, regenerated whenever the spec changes. Use into to nest the reference under an existing page. Do not recreate a spec with insert_page: openapi-operation blocks in a document only render the operations they name. When page content is provided as markdown, links to other pages are resolved against the change request's current page tree. You can link a page by:
Page ID —
/pages/<pageId>(also/pages/<pageId>#anchor).Page slug path —
/pages/<slug>for a top-level page, or/pages/<parent-slug>/<slug>for a nested page (e.g./pages/guides/getting-started).Markdown file path — the path the page would have in Git:
/pages/<slug>.mdfor a page, or/pages/<slug>/README.mdfor a page that has child pages. The space's root page is/pages/README.md.Relative markdown link — resolved against the page being edited, e.g.
./sibling.md,../other.md, or../README.md(the parent page). Folder-style links without an extension (e.g../section) resolve to that folder's page.
External URLs (https://…) are kept as-is. A link that does not match any page in the change request is left as plain text rather than causing the request to fail. GitBook normalizes submitted content, so the stored page may differ from what was sent. To help callers reconcile local state with the stored result, the response can list each created and modified page with its resolved ID and a urls.location to fetch the page's persisted content. For change requests with frequent concurrent edits, consider expectedRevision to protect against overwriting changes made since your read. If the branch has changed since the supplied revision, the batch is rejected with HTTP 409 and none of the submitted changes are applied. Response shape (compat) — this endpoint is changing what it returns:
compat=true(current default) returns the updated change request, matching the endpoint's original behavior.compat=falsereturns the updated change request and the list of affected pages (see the response schema).
The default is true for now to preserve backwards compatibility, but will switch to false in a future release, at which point the affected-page list is always included. You should pass compat=false if you want the new behavior today and avoid unexpected errors when the default flips in the future.
A unique entity identifier
^[a-zA-Z0-9_-]+$A unique entity identifier
^[a-zA-Z0-9_-]+$When true, the updated change request is returned. When false, the updated change request and the list of affected pages are returned.
trueThe saved branch revision on which these changes are based, available in getChangeRequestById's revision field. If the branch has changed, including pending realtime edits, the batch is rejected with 409 and none of the submitted changes are applied. This field is optional and most changes can omit it.
The change request has been updated
The change request when compat=true (default), or the change request plus the list of affected pages when compat=false.
The change request could not be found.
The branch changed since the expected revision; read the latest content before retrying.
POST /v1/spaces/{spaceId}/change-requests/{changeRequestId}/content HTTP/1.1
Host: api.gitbook.com
Authorization: Bearer YOUR_SECRET_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 200
{
"expectedRevision": "text",
"changes": [
{
"operation": "update_page",
"page": "page_id",
"document": {
"markdown": "# Hello World\n\nThis is a markdown document linking to [another page](/pages/page_id).\n"
}
}
]
}{
"object": "change-request",
"id": "text",
"number": 1,
"status": "draft",
"subject": "text",
"description": {
"object": "document",
"data": {},
"nodes": [
{
"object": "block",
"type": "paragraph",
"data": {},
"nodes": [
{
"object": "text",
"leaves": [
{
"object": "leaf",
"text": "Hello world",
"marks": []
}
]
}
]
}
]
},
"createdBy": {
"object": "user",
"id": "text",
"displayName": "text",
"email": "text",
"photoURL": "text",
"urls": {
"location": "https://example.com"
}
},
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"mergedBy": {
"object": "user",
"id": "text",
"displayName": "text",
"email": "text",
"photoURL": "text",
"urls": {
"location": "https://example.com"
}
},
"mergedAt": "2026-01-01T00:00:00.000Z",
"space": "text",
"revision": "text",
"revisionInitial": "text",
"revisionMergedAncestor": "text",
"revisionMerged": "text",
"comments": 1,
"changeCounts": {
"pages": {
"added": 1,
"modified": 1,
"deleted": 1
},
"library": {
"added": 1,
"modified": 1,
"deleted": 1
},
"blocks": {
"added": 1,
"modified": 1,
"deleted": 1
},
"blocksOmittedReason": "too-many-pages"
},
"siteTopic": {
"site": "text",
"topic": "text"
},
"siteFindings": [
{
"site": "text",
"finding": "text"
}
],
"styleguide": {
"site": "text",
"run": "text",
"changedPages": 1,
"checkedAt": "2026-01-01T00:00:00.000Z"
},
"outdated": true,
"imports": [
{
"object": "import-run",
"id": "text",
"startedAt": "2026-01-01T00:00:00.000Z",
"completedAt": "2026-01-01T00:00:00.000Z",
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"status": "pending",
"reason": "text",
"source": {
"type": "website",
"url": "https://example.com"
},
"target": {
"space": "text",
"changeRequest": "text",
"page": "text"
},
"site": "text",
"createdBy": "text",
"urls": {
"location": "https://example.com"
},
"pages": [
{
"id": "text",
"sourceURL": "text",
"type": "page",
"status": "complete"
}
]
}
],
"links": [
{
"id": "text",
"ref": {
"kind": "url",
"url": "text"
},
"title": "text",
"isSyncing": true,
"expanded": {
"kind": "external-ticket",
"provider": "jira",
"state": "connected",
"key": "text",
"url": "https://example.com",
"status": {
"id": "text",
"name": "text"
},
"summary": "text"
}
}
],
"urls": {
"app": "https://example.com",
"location": "https://example.com"
}
}Last updated
Was this helpful?