Developer docs
CLI & GitHub integration
Engineers build from files in the repo, not from a browser tab. The versodocs CLI pulls the approved (Current) version of a spec into the repo as Markdown, records the exact version in versodocs.lock, and tells CI when that version has been superseded.
Install
Node.js 20 or later, no dependencies. Run it with npx or add it to the project:
npx -y @versodocs/cli@0 --help
# or
npm install --save-dev versodocsThe CLI uses the REST API and works on every plan. Reading past versions (pull --version, pull --frozen) and diff need Team; on Free, check still runs and says which plan adds change summaries.
Log in
versodocs login --org <org-slug> [--api-url https://versodocs.com]Create an API key in VersoDocs under Settings → Developer (scope docs:read). login asks for it with hidden input, or reads it from --token or stdin (echo "$TOKEN" | versodocs login --org acme). It is stored in ~/.config/versodocs/config.json ($XDG_CONFIG_HOME respected; %APPDATA%\versodocs\config.json on Windows) with file mode 600. versodocs logout deletes it.
| Setting | Flag | Env | Default |
|---|---|---|---|
| Token | login --token | VERSODOCS_TOKEN | — |
| Org | --org | VERSODOCS_ORG | from versodocs.lock, then login |
| API URL | --api-url | VERSODOCS_API_URL | https://versodocs.com |
VERSODOCS_TOKEN always wins over the stored token, so use it in CI. A stored token is only ever sent to the API URL it was saved for.
Commands
pull
# One spec (path = <workspace>/<folder…>/<slug>)
versodocs pull eng/specs/checkout-api [--out docs/checkout-api.md] [--version 12]
# Every spec in a folder; subfolders are mirrored under --out-dir
versodocs pull --folder eng/specs --out-dir docs/specs
# Everything in versodocs.lock, updated to the latest Current version
versodocs pull
# Everything in versodocs.lock at exactly the locked versions (restore)
versodocs pull --frozenEach file is the published Markdown with YAML front-matter:
---
title: "Checkout API"
versodocs_id: "0b6c2d8e-1f2a-4b3c-9d4e-5f6a7b8c9d0e"
version: 13
published_at: "2026-10-01T09:12:00.000Z"
url: "https://versodocs.com/org/acme/eng/specs/checkout-api"
reference: "versodocs:acme/0b6c2d8e-1f2a-4b3c-9d4e-5f6a7b8c9d0e@v13"
---
# Checkout API
…Without --out, a single spec is written to <slug>.md in the current directory; --out-dir defaults to .. --summary-file <file> writes a Markdown table of what changed (doc, old → new version, link); the auto-sync workflow uses it as the pull request body.
status
FILE LOCKED CURRENT STATUS
-------------------------- ------ ------- ----------
docs/specs/checkout-api.md v12 v13 outdated
docs/specs/payments.md v4 v4 up to dateStatuses: up to date, outdated (a newer version is Current), missing (file deleted), modified (file edited locally since the pull), not found (document deleted or unpublished). --json prints machine-readable rows.
check
Same table; exits 1 when any spec is outdated, missing or not found, and prints a summary of what changed for each outdated spec (Team+).
diff
versodocs diff docs/specs/checkout-api.md # or the doc path: eng/specs/checkout-apiSummary and Markdown diff between the locked version and Current (or --version N). Team+
API endpoints used
| Command | Endpoint | Plan |
|---|---|---|
pull <path> | GET docs/by-path?format=md | All plans |
pull | GET docs?folder=…, GET docs/{id}?format=md | All plans |
pull --version / --frozen | GET docs/{id}?version=N | Team+ |
status, check | GET docs/{id}/current (falls back to GET docs/{id} on Free) | All plans |
diff, check summary | GET docs/{id}/diff | Team+ |
versodocs.lock
Commit it. It records the org, API URL, folders pulled with --folder, and for every spec its id, path, version, file and the sha256 of the file as written. Entries are sorted and the JSON is formatted deterministically, so it diffs cleanly in pull requests.
{
"lockfile_version": 1,
"api_url": "https://versodocs.com",
"org": "acme",
"folders": [{ "folder": "eng/specs", "out_dir": "docs/specs" }],
"docs": [
{
"id": "0b6c2d8e-1f2a-4b3c-9d4e-5f6a7b8c9d0e",
"path": "eng/specs/checkout-api",
"version": 13,
"file": "docs/specs/checkout-api.md",
"sha256": "…"
}
]
}CI check
Fail a build when the repo's specs fall behind:
- run: npx -y @versodocs/cli@0 check
env:
VERSODOCS_TOKEN: ${{ secrets.VERSODOCS_TOKEN }}GitHub auto-sync
A ready-to-copy workflow keeps the specs in your repo on their Current version. Every hour, or on demand, it runs versodocs pull against versodocs.lock and, when a spec has a new published version, opens (or updates) one pull request titled Update specs from VersoDocs. Nothing changes in your repo until someone merges it. Works on every plan.
- Pull your specs once, locally, so the repo has a lockfile:
bash npx -y @versodocs/cli@0 login --org <your-org> npx -y @versodocs/cli@0 pull --folder <workspace>/<folder> --out-dir docs/specs git add versodocs.lock docs/specs && git commit -m "docs: add specs from VersoDocs" - Add an API key (
docs:read) as the repository secretVERSODOCS_TOKEN. - Copy
integrations/github-sync/versodocs-sync.ymlto.github/workflows/. - Settings → Actions → General → Workflow permissions → enable Allow GitHub Actions to create and approve pull requests.
on:
schedule:
- cron: '17 * * * *' # hourly
workflow_dispatch:
repository_dispatch:
types: [versodocs.published]
permissions:
contents: write
pull-requests: write
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npx -y @versodocs/cli@0 pull --summary-file "$RUNNER_TEMP/versodocs-changes.md"
env:
VERSODOCS_TOKEN: ${{ secrets.VERSODOCS_TOKEN }}
- uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
branch: versodocs/sync
commit-message: 'docs: update specs from VersoDocs'
title: Update specs from VersoDocs
body-path: ${{ runner.temp }}/versodocs-changes.md- The workflow reuses one branch (
versodocs/sync), so you get one rolling PR. - Folders pulled with
--folderare re-listed on every sync, so documents added later arrive in the next PR. - PRs opened with the default
GITHUB_TOKENdon't trigger other workflows (GitHub's rule); pass a PAT or GitHub App token via the action'stoken:input if CI must run on them.
Spec drift check (pr-check)
Catch pull requests built from an outdated spec. versodocs pr-check scans the PR title and description for VersoDocs references, asks which version is Current, and fails (or warns) when the PR points at an older one. Team+
"Payments API": PR references Spec v12 but Current is v13: https://versodocs.com/org/acme/eng/proposals/…Reference a spec by pasting Copy reference for PRs from any document page (versodocs:<org>/<documentId>@v<N>), or a document URL with ?v=<N>. On GitHub, the GitHub App runs the same check with no workflow file.
name: Spec drift check
on: { pull_request: { types: [opened, edited, synchronize, reopened] } }
jobs:
spec-check:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npx -y @versodocs/cli@0 pr-check
env:
VERSODOCS_TOKEN: ${{ secrets.VERSODOCS_TOKEN }}| Option | Default | Description |
|---|---|---|
VERSODOCS_TOKEN (env) | required | API key (docs:read). Only ever sent to --api-url. |
--api-url | https://versodocs.com | VersoDocs origin. URL references on other hosts are ignored. |
--mode | fail | fail: drift or an unknown reference fails the job. warn: report only, always exit 0. |
--require-reference | off | Treat a PR without any spec reference as a problem. |
--text / --file | PR title + body | Scan this text / file instead. |
On GitHub Actions it adds ::error / ::warning annotations, a job-summary table and the references / outdated step outputs. Network errors and VersoDocs 5xx responses only warn, so an outage never blocks merges; a rejected token (401), a plan without the CI check (403) or an unknown document (404) is reported as a problem. Include edited in the triggers so fixing the reference re-runs the check. Pull requests from forks get no secrets, so skip them with if: github.event.pull_request.head.repo.full_name == github.repository.
spec-check:
image: node:22
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- npx -y @versodocs/cli@0 pr-check
# VERSODOCS_TOKEN: masked CI/CD variableErrors & exit codes
| HTTP | What the CLI tells you |
|---|---|
| 401 | Run versodocs login --org <slug> or set VERSODOCS_TOKEN. |
403 plan_feature | Which plan unlocks the feature, and the upgrade URL. |
| 404 | Check the org slug / path and that the document has a published version. |
| 429 | Retried automatically (honours Retry-After up to 30 s), then reports the wait. |
Exit codes: 0 ok, 1 failure (check found outdated specs, pr-check found drift in fail mode), 2 usage error (bad flags, no token, nothing to scan).