The command line, and forumulate doctor
Two packages are published on npm. @forumulate/sdk is a typed client for the API; @forumulate/cli is a command you run in a terminal, and it includes doctor — a check of a live site from the outside, which is the only vantage point that tells you what your readers actually get.
The Python and Go SDKs, the MCP server and the website integrations are in SDKs and integrations.
Install
npm install -g @forumulate/cli # or: pnpm add -g @forumulate/cli
forumulate helpNothing to install, one command:
npx --yes @forumulate/cli doctor https://docs.yourcompany.comnpx is the form to use in CI and the form to paste into a ticket. The package depends on @forumulate/sdk, which npm installs for you.
Point it at an API
Every command except doctor and conformance talks to the API and needs a token. forumulate login asks for one (it is not echoed), checks it, and stores it for this API in ~/.config/forumulate/credentials.json, readable only by you. In CI, set FORUMULATE_TOKEN instead; it always wins over a stored token.
Variable | What it is |
|---|---|
| A personal access token, sent as |
| The site, default |
| The exact API base, for a local API with no |
forumulate login
forumulate whoami
forumulate config set org acme # the default --org for every commandTwo flags apply to every command: --json prints the raw JSON instead of the formatted summary, and --version prints the CLI version. Every command exits non-zero on failure.
Command reference
Resource commands
Every group takes the same verbs — list, get, create, update, delete, and publish where a thing has a draft — and the same scope flags: --org, --space and --library, each an id or a slug. forumulate <group> --help prints a group's verbs and the API operation each one calls.
Command | What it does |
|---|---|
| Your organizations, their DeepL key, and their own wording for interface strings. |
| People in an org. |
| Invitations you send (one, many, or a shareable link) and the ones waiting for you. |
| Groups and join requests, custom roles, and non-human members with their tokens. |
| Your personal access tokens, by id or by name. |
| Endpoints, their deliveries, and replays. |
| Installed extensions and the marketplace; the audit log (export as CSV). |
| Spaces in an org: slugs, moves, copies and the trash. |
| Space snapshots, and a space's shape override. |
| Any piece in a space. |
| A docs space's pages: drafts, revisions, scheduled publishing, rollback, the trash, imports and languages. |
| Collections and their page order; docs versions. |
| Proposed docs changes, review and merge rules. |
| OpenAPI specs behind reference pages, and the SDKs generated from them. |
| Publishing those SDKs to package registries. |
| Articles, topics and releases; scheduling, pinning, settings, flags, polls, replies. |
| One piece's translations at its own door. |
| A roadmap space. |
| A status space: incidents, components, where they are announced. |
| Events and RSVPs, course progress, job applications, forms and submissions (CSV export). |
| Headless CMS libraries: grants, languages, the model, delivery keys. |
| A library's entries. |
| Library media, content releases, and sandbox environments. |
| Custom domains on a space (by id or hostname), and its uploaded files. |
| A space's theme: kept in git with |
| Sites: several spaces under one domain and theme. |
| Sync a docs space with a repository. |
| What a reader is served, as the public API answers it ( |
| What each kind of space is. |
| Unified search across docs, forum and blog, and the searches that found nothing. |
| Publish a folder of Markdown — see Publish from git. |
| Content as code: the folder publish as a plan unless |
| Take a space version snapshot. |
| Headless CMS models, and imports from other CMSs — see Importing. |
| Run the Content Sync Protocol suite against any platform's collection endpoint. |
| Check a live site from the outside (below). |
| Defaults: |
| Shell completion: |
The flags of a resource command come from the API: every query parameter of its operation (--limit, --state) and every top-level body field (--name, --visibility) is a flag. delete, remove and revoke ask before they send; --yes skips the question and is required where there is no terminal. --dry-run prints the request instead of sending it, and --paginate walks a list to its end. A command whose answer is a file (an SDK archive, a PDF, a CSV export) writes it to --out <file>; one that uploads (assets upload, media upload, docs import zip) takes the local path as its argument.
The older positional forms — spaces <orgId>, items <orgId> <spaceId>, search <orgId> <query…>, domains [verify] <orgId> <spaceId> — still work and print what they always did.
forumulate api: every operation
The resource commands cover the daily work. forumulate api reaches every operation in the API reference, from a copy of the OpenAPI document bundled with the CLI:
forumulate api list --tag Webhook --search replay # discover: operationId, method, path, summary
forumulate api describe Space_create # parameters as flags, the body as fields
forumulate api Space_list acme --limit 20 # path params positionally, or --orgId acme
forumulate api GET /orgs/{orgId}/members --paginate --json
forumulate api POST /orgs/acme/spaces --data @space.json
forumulate api Space_update acme <spaceId> --field settings.overview="Start here"Input | How to give it |
|---|---|
Path parameters | Positional, in path order, or |
Query, header params |
|
A JSON body |
|
A top-level body field |
|
A multipart upload |
|
A name we also use |
|
Output is a table for a list (with a footer naming the next --cursor), and key value lines for a single resource; --json prints the body exactly as received, --paginate follows page.nextCursor and prints one merged list.
Every request carries Forumulate-Version: 2026-12-01 (pin another with --api-version or forumulate config set api-version), and every write that declares one carries an Idempotency-Key — the same key on a retry, so a retried create is not a second create. A 429 is retried after its Retry-After; a 502, 503 or 504 is retried only for a request that is safe to repeat. A route newer than your CLI still works as forumulate api <METHOD> <path>, sent as written.
doctor
npx --yes @forumulate/cli doctor https://docs.yourcompany.comIt takes one URL and nothing else, speaks plain HTTP, and reports the same way whether you point it at a Forumulate space, at a staging mount, or at a site running on something else entirely. A check that only works on its author's deployment is a checklist with extra steps.
Every one of these checks exists because a customer discovered the failure after going live.
Check | Passes when | Fails when, and why that matters |
|---|---|---|
|
| A plain-text 500 reads as a permanent bug, so a careful client stops retrying a few-minute outage. |
| An anonymous | A public page that redirects to sign-in is a login wall; crawlers index nothing. |
| The document has a | Search engines credit the canonical host, so a platform URL here hands your authority to the platform. Skipped when there is no HTML to read. |
| The markup contains no | Cloudflare Email Obfuscation is rewriting addresses on the page. The decoder it injects carries no CSP nonce, so the browser refuses it and every address renders as |
| The document carries a | Without one, every reader pays full origin latency and a CDN in front of it does nothing. |
|
| A |
|
| A crawler finds pages faster when it is handed the list. |
| A request sent with a Googlebot User-Agent gets the same status as the anonymous one. | A WAF rule or bot challenge that answers crawlers differently removes the site from search without telling anybody. |
|
| Skipped, not failed, when there is no feed — a documentation space is not expected to have one. |
A healthy run against our own guides:
Checking https://forumulate.com/guides
PASS health Health endpoint answers JSON
200 with a JSON body.
PASS public-read A logged-out reader can read the page
307 → /guides/getting-started
PASS canonical Canonical link points at this host
https://forumulate.com/guides
PASS html-untouched No proxy is rewriting the markup
No rewritten email addresses.
PASS cache-headers The document carries a usable Cache-Control
public, s-maxage=60, stale-while-revalidate=60, max-age=0, must-revalidate
PASS robots robots.txt allows this path
Allows /guides
PASS sitemap robots.txt names a sitemap
Declared.
PASS crawler-access A crawler gets the same answer as a reader
reader 307, crawler 307
SKIP feeds A feed is served under this path
404 for /guides/rss.xml
8 passed, 0 failed, 1 skippedExit codes
Code | When |
|---|---|
| No check failed. Skips do not fail the run. |
| At least one check failed, or you gave no URL, or the command is unknown, or the run threw. |
So doctor belongs in CI exactly as it is:
npx --yes @forumulate/cli doctor https://docs.yourcompany.com || exit 1One thing to watch
doctor sends its health check to FORUMULATE_URL when that variable is set, and to the site's own origin when it is not. If you have FORUMULATE_URL pointing at a local API from an earlier command in the same shell, the health check reports on that API rather than on the site you named. Unset it, or run doctor in its own shell.
--json
npx --yes @forumulate/cli doctor https://docs.yourcompany.com --jsonThe object has four top-level keys beside url: checks (each with id, title, state, detail and matters), passed, failed and warnings. warnings is the count the formatted output prints as "skipped".
conformance
npx --yes @forumulate/cli conformance https://docs.yourcompany.com/_api/…/collections/…Runs the Content Sync Protocol suite against a collection endpoint. It is deliberately platform-neutral: point it at us, or at whatever you are migrating from, and compare.
In the API
Every operation below is also reachable from the other surfaces without a dedicated command. forumulate api <Operation> calls it from the CLI, the JavaScript SDK has a typed method for each one (the last column names it; the Python and Go clients are generated from the same spec), and an agent reaches the organization-scoped ones through the MCP call_api tool. Where a CLI command or MCP tool exists too, the last column names it. Operation names link to the API reference.
Member
Operation | Request | What it does | Shortcuts |
|---|---|---|---|
| List members in an organization |
| |
| Create a member in an organization |
| |
| List invites in an organization |
| |
| Delete the invite (members) |
| |
| Create the bulk in an organization (members) |
| |
| List invite links in an organization |
| |
| Create an invite link in an organization |
| |
| Delete the invite link |
| |
| Update the member |
| |
| Delete the member |
| |
| Revoke the session |
| |
| Create the reassign content in an organization |
|