Documentation Menu

Management API

6 min read

The management API under /api/v1 is how the dashboard, the CLI and your own scripts write content, publish it and run a project.

Why a script can do what the dashboard does

The dashboard has no private back door. Every save, publish and setting you see there is a request to this API, and so is every command the CLI runs. So anything a person does in the dashboard, a script can do too, with the same validation and the same refusals. The routes, their parameters and the shape of every answer are written down in one contract, /openapi.json. This site serves it as an exact copy of the CMS's own. Generate a client from it, or keep it open beside this page.

What the routes cover and how they refuse

The surfaces

Every path below sits under /api/v1/projects/<project>, and <env> stands for an environment's key. Authentication lists which credential each route takes.

Surface Routes
Documents envs/<env>/documents, envs/<env>/documents/<key> (GET, PUT, DELETE), envs/<env>/references/<key>
Versions envs/<env>/versions/<key>, newest first, bodies only with ?bodies=1
Publishing envs/<env>/publish, envs/<env>/unpublish, envs/<env>/publish-impact/<key>
Schemas documents under _schemas/, plus envs/<env>/schemas, envs/<env>/schemas/<name>/validate, envs/<env>/types
Translations envs/<env>/translations/<key>, envs/<env>/translations/<key>/review, envs/<env>/work-queue
Assets assets (upload, list), assets/<id>, assets/<id>/references; folders for the media library
Environments environments, environments/<env>, and envs/<env>/preview-tokens
Promotion envs/<env>/compare, envs/<env>/promote, envs/<env>/promotions
Sync envs/<env>/sync/diff and envs/<env>/sync/bulk, what quillet push and pull use
Import import, the staged restore quillet restore drives
Keys keys
Webhooks webhooks and its deliveries, on Webhooks
Search envs/<env>/search/reindex

Requests and answers are JSON. A list that can grow is paged. Send limit, then pass the nextCursor of one answer as cursor to get the next page, and stop when it is null.

Saving is not publishing

PUT …/documents/<key> appends a version and moves the draft. It never changes what delivery serves; only publish does that. If you send a body equal to the current one, nothing happens, and no empty version is created. Every document answer carries version and contentHash, the hash of the current version's content.

POST …/publish takes up to 200 documents and makes them live in one transaction, at one instant. publish-impact/<key> tells you what a publish would change, without doing it. It answers with the version that would go live, the live pages that include the document, and the problems a publish would refuse with.

Two writers at once

To make a PUT conditional on the version you read, send If-Match: W/"<contentHash>" with it. If someone has saved since you read, the answer is 409 conflict and nothing is written. A header the API could not have issued, such as *, gets a 422 instead. Without the header, the save overwrites whatever is there, and that is what quillet content put does when you leave out --if-match. A CLI push is conditional in its own way. It sends each document's baseHash, and it refuses on a conflict unless you pass --force. A publish entry may carry version in the same spirit. If the draft has moved past that version, the whole batch is refused with a 409 that names both versions.

Refusals

Status error Meaning
401 unauthorized No credential, or one the API does not recognise.
403 forbidden The credential cannot do this; message says why.
404 not_found No such thing, or none this credential may know about.
409 conflict, in_use A lost race (read again and retry), or something live still refers to what you changed; usedBy names it.
413 payload_too_large The body is larger than the server accepts. Split the request.
422 validation_failed fields holds one sentence per offending input, keyed by its dotted path, such as documents.3.content.title. _form is about the body as a whole.
429 rate_limited The caller spent its budget for the current window.

A 422 is always safe to show, because every value in fields is a sentence written for the person who sent the request.

Rate limits

The management API has no general request budget. Sign-in and uploads each have a budget you can configure, and so do compare, promote and clone; Configuration lists them. Three budgets are fixed. Preview tokens are limited to thirty a minute per credential, webhook test events to ten a minute per webhook, and a search reindex to five an hour per environment.

How to

API

Save a draft, using a CLI key that holds content:write:

curl -s -u '<keyId>:<secret>' -X PUT \
  -H 'Content-Type: application/json' \
  -d '{"type":"md","title":"Introduction","content":"Welcome.","message":"First draft"}' \
  'http://localhost:3000/api/v1/projects/<project>/envs/<env>/documents/<key>'

The answer is the document, with its version and contentHash. Now save again, on condition that nobody else has saved in between, by sending the hash you were given:

curl -s -u '<keyId>:<secret>' -X PUT \
  -H 'Content-Type: application/json' \
  -H 'If-Match: W/"<contentHash>"' \
  -d '{"type":"md","title":"Introduction","content":"Welcome, again."}' \
  'http://localhost:3000/api/v1/projects/<project>/envs/<env>/documents/<key>'

Run the same request a second time. The hash is no longer the current one, so the answer is {"error":"conflict",…} with status 409, and the document stays as it was.

Publish the document, using a key that holds content:publish:

curl -s -u '<keyId>:<secret>' -X POST \
  -H 'Content-Type: application/json' \
  -d '{"documents":[{"key":"<key>"}]}' \
  'http://localhost:3000/api/v1/projects/<project>/envs/<env>/publish'

Each entry in results says "status":"published", with the version that is now live. Then read the document's history:

curl -s -u '<keyId>:<secret>' \
  'http://localhost:3000/api/v1/projects/<project>/envs/<env>/versions/<key>?limit=5'

CLI

The CLI sends the same requests. Sign in as Install and sign in shows, with a .quilletrc that names the project and the environment. Then put a file as a draft:

npx quillet content put <key> --file <file.md> --message "First draft"

Publish it. At a terminal, publish shows what else changes and asks before it goes ahead. --yes answers for you:

npx quillet publish <key> --yes

--verbose prints each request's method, path and status, so you can see which route sits behind a command.

Limits

  • If-Match covers a document's content only. If somebody else changes the title, the tags or the schema binding, your save is not a conflict.
  • There is no merging. A 409 is a decision for a person to make: read again, compare, and save.
  • One publish takes at most 200 documents, and one promotion at most 500 entries.
  • There is no scheduled publishing, no approval step and no named release.
  • JSON_BODY_LIMIT caps a request body, and ASSET_MAX_BYTES caps an upload. Both are on Configuration.
  • Keys, webhooks, members, changes to project settings, and creating, editing or deleting environments refuse every API key, whatever its scopes. Reading the project and its environments works with any CLI key of the project.
  • The API has one version, v1, and no GraphQL endpoint.

Last updated