Skip to content

REST API

CompassDocs ships a small, stable REST API at /api/v1 for scripting and integrations: CI jobs that update runbooks, exporters, dashboards, or anything else that shouldn’t need a browser session.

Every request needs a personal access token in the Authorization header:

Terminal window
curl -H "Authorization: Bearer cdk_…" https://docs.example.com/api/v1/me

Create tokens under your name → Manage account → API tokens. Each token has scopes chosen at creation:

Scope Grants
read All GET endpoints — documents, spaces, search.
write Creating, editing, and trashing documents.

A token can never do more than the person it belongs to: role, space access, and the approval workflow all still apply. Uncheck Allow writes when creating a token to mint a read-only one — enough for search and exports, and the safer default for anything that only consumes content. (The Claude connector needs a read + write token.)

Requests are rate-limited to 120/minute per user; over the limit you get 429. Tokens are shown once at creation, stored only as a hash, and revocable any time from the same screen.

Token introspection — the user it acts as, their role, and the token’s scopes.

Spaces visible to the token’s user, with slugs (used as the space parameter elsewhere), visibility, and document counts.

List documents, newest first. Query parameters:

Parameter Meaning
space A space slug (e.g. engineering).
status published or draft — drafts are visible to editors and up.
limit / offset Paging; limit caps at 100 (default 25).

Returns {items, total, limit, offset}. List items omit content; fetch a single document for the full body.

From 1.18.0 this list, GET /api/v1/search and the document counts of GET /api/v1/spaces leave out handbook-only documents in spaces the token’s user can’t write in, as the app’s listings do. GET /api/v1/documents/:id still returns one to anyone who can read its space: it hides documents from listings, it doesn’t restrict them.

Create a document (needs write + the editor role + edit rights in the space):

Terminal window
curl -X POST https://docs.example.com/api/v1/documents \
-H "Authorization: Bearer cdk_…" -H "Content-Type: application/json" \
-d '{"space":"engineering","title":"Deploy runbook","content":"…",
"type":"sop","tags":["deploy"],"status":"published"}'

type is one of sop | technical | policy | knowledge (default knowledge). In a strict-approval workspace, an editor asking for published gets a draft back with a note — publishing still goes through review, same as the app.

One document, content included. From 1.11.0 the document also carries ack_required (whether readers must confirm it), ack_edit_default (ask, material or reset: what the document does when its text changes, see the edit default) and ack: { confirmations, eligible, version }, the standing an integration needs before it edits a policy: how many people have confirmed the version everyone was last asked to confirm, how many are expected to, and that version as Version history numbers it ("v12", or null before the first version). The counts are facts whether or not the requirement is on.

Partial update — send any of title, content, summary, tags, type, status, plus an optional note (used as the version note). If the change touches a published document and the token’s user can’t publish, the change is queued as a change request and you get 202 {pending: true, change_request_id} — nothing goes live until an approver signs off.

Editing a policy. A change to the title or content of a published document that requires read confirmation, or a status: "published" that puts such a draft live when its text differs from the version people confirmed, must say what readers should see: send ack_policy ("keep" for an editorial fix, which leaves every confirmation standing; "reset" for a material change, which asks everyone to re-confirm and tells them) with ack_reason (a reset needs a reason of at least 10 characters; a keep follows Program settings’ Require a reason for editorial fixes too; longer reasons are cut at 500; recorded on the compliance record and, for a reset, shown to readers; it is also the version note when note is absent). Without ack_policy the call is refused with 428 Precondition Required and nothing is written:

{
"error": "This document requires read confirmation. Pass ack_policy \"keep\" (editorial fix) or \"reset\" (material change), with ack_reason.",
"decision_required": true,
"confirmations": 42,
"eligible": 50,
"baseline": "v12",
"change": { "pct": 1.1, "wordsChanged": 14, "blocksChanged": 1, "headingsChanged": 0, "numbersChanged": 0 },
"suggested": "keep"
}

confirmations and eligible are the standing, baseline the version people confirmed, change the measured difference between that version and what you sent (the same measure the editor’s question shows), and suggested what that measure calls it. Show them to a person and call again with the answer. A policy without a usable ack_reason (a reset with fewer than 10 characters, or a keep with none while the setting is on) answers 400 ack_reason is required when the text of a confirmed policy changes.; any other value of ack_policy answers 400 ack_policy must be "keep" or "reset"., and the field is validated whenever it is sent, so a bad value is refused even on a change that is not substantive. A PATCH that only touches summary, tags, type or moves a published document back to draft is never refused for want of a decision, and neither is an edit to a draft that stays a draft (the decision is taken when it is published), nor any edit to a document that does not require read confirmation. The response carries the document with the fields GET returns and ack_outcome: { changed, substantive, reset, confirmations_reset, policy, source }, so a script can log what its edit did. When the token’s user cannot publish, the change is queued as before; ack_policy and ack_reason travel with the change request as the submitter’s recommendation and the approver decides (see the review queue).

Tokens never confirm. A token may edit a policy and must decide keep or reset when it does; it can never record a read confirmation. POST /api/documents/{id}/ack and POST /api/documents/{id}/ack/reconfirm accept the signed-in session only and answer 401 to any bearer, however valid, and the connector has no tool that confirms. That is deliberate: a confirmation is a person’s attestation, not an integration’s.

Moves the document to the Trash (restorable in the app; permanent deletion follows the workspace retention setting).

The same hybrid search as the app — full-text always, fused with semantic retrieval when an embeddings provider is configured — including the type: tag: space: author: status: operators:

Terminal window
curl -H "Authorization: Bearer cdk_…" \
"https://docs.example.com/api/v1/search?q=failover+space:engineering"

Snippets come back as plain text.

Errors are JSON with a human-readable message: 401 (missing/invalid token), 403 (missing scope, role, or edit rights), 404 (not found — including documents outside the user’s space scope), 429 (rate limit), 400 (malformed input), 428 (a change to the text of a document that requires read confirmation was sent without ack_policy; the body says what to send).

The helpdesk (enterprise) has its own REST API from 1.19.4, under /api/helpdesk/v1, with its own tokens that act as a desk role. See API tokens and webhooks.

For AI-driven use, the Claude connector exposes the full editing surface over MCP — the REST API is the better fit for scripts and conventional integrations.