Skip to main content
nohead is the command-line interface for Nohead, for developers, CI and coding agents. It calls the public API through the TypeScript SDK, so it can do what an API key can, and every change it makes is recorded in history as made via the CLI. The command reference lists every command; nohead <command> --help prints the same for one.

Install

The CLI needs Node 26 or newer.
Pin a version in CI, such as npx nohead@0.5, so an upgrade is a change you choose. Until 1.0, any release may break scripts that use the CLI; its releases mark what under Breaking.

Sign in

nohead login shows a code and opens the web app, where you check the code matches and choose the project, the API key’s scopes and its expiry. The CLI then saves the new key, named after your machine, for its project, in a file only you can read. Over SSH or without a browser, open the link it prints on any device. In a folder linked with nohead link, or with --project or NOHEAD_PROJECT, the key is for that project: the page offers no other. Only approve a code your own terminal just showed: never approve a sign-in from a link someone sent you. The saved key is for the CLI alone: the SDKs read NOHEAD_API_KEY from the environment, not the CLI’s keys. For an app, create a key under API keys in the web app and set it as NOHEAD_API_KEY in the app’s environment. An API key belongs to one project, so to work on several, log in once for each: the keys are saved side by side. Approving another sign-in from the same machine for the same project replaces the key: the CLI saves the new one, and the earlier one is revoked. To save a key you already have, pipe it in: echo "$NOHEAD_API_KEY" | nohead login, or from the clipboard, pbpaste | nohead login on macOS (wl-paste or xclip -o on Linux, Get-Clipboard in PowerShell). nohead logout forgets the saved key of the project in use; the key itself stays valid until you revoke it in the web app, or approve another sign-in from that machine for that project.

Projects

Choosing a project chooses its saved key. A command works in the first of:
  1. --project <id>
  2. NOHEAD_PROJECT
  3. the project in the nearest nohead.json, in the current folder or a folder above it
  4. the project of the only saved key
With keys saved for several projects and none chosen, a command exits with 2 and lists the projects and the ways to choose one. nohead whoami shows the project in use and where it came from.
nohead link writes nohead.json, { "project": "prj_..." }, in the current folder. It holds no secret, so commit it: each teammate logs in once with a key of that project, and a coding agent working in the folder stays in its project too. To unlink a folder, delete its nohead.json. In CI, or for an agent that should act with a key of its own, set NOHEAD_API_KEY instead of logging in. It wins over saved keys and works in its own project (or the one given with --project or NOHEAD_PROJECT); nohead.json doesn’t apply to it.

Configuration

Flags win over environment variables, which win over nohead.json and saved keys.

Output and exit codes

Scripts and agents can rely on this:
  • stdout carries only results. With --json, exactly one JSON document, the API’s representation; records export writes JSON Lines.
  • stderr carries errors, summaries and diagnostics. With --json, errors use the API’s error envelope, { "error": { "type", "message", "request_id", "details" } }.
  • Exit codes: 0 success, 1 an API error (validation, authentication, permission, not found, a stale revision, a plan limit, import failures), 2 a usage error (bad arguments, no API key, unreadable files), 3 the API or storage unreachable, 4 an unexpected failure in the CLI (NOHEAD_DEBUG=1 prints the stack).
  • Retries: up to 3, reported on stderr. A 429 is retried after its Retry-After (up to 60 seconds); network failures and 500, 502, 503 and 504 with backoff. Every write carries an Idempotency-Key, so a retry never writes twice.
  • Deleting a collection and discarding a draft need --yes; a discarded draft can’t be restored. Everything else is soft-deleted and can be restored.

Lists and filters

A list prints one page and ends with More: --cursor <cursor> when there are more. A page holds 100 (audit: 20), and --limit n sets another size, up to 100. search prints the top 20 results, or --limit n up to 100, without pages. --cursor <cursor> prints the next page, and --all follows the cursors and prints everything. With --json, a page prints as the API returns it (data and meta.next_cursor), and --all as { "data": [...] }. --filter takes key=value, key!=value, key<value (and >, <=, >=), "key in a,b" (\, for a comma in a value), "key exists" and "key !exists". Several on one key combine:
Dates take today, and dates with a time now. records list --status published (or unpublished) filters by published_at; across a whole project, search filters with "published_at exists" (or !exists). Reading content has how filters match.

Records and drafts

Record data comes from --set key=text, --set key:=json (numbers, booleans, null, lists, objects), --data '{...}' and --data-file <path|->. update changes only the fields given; --set key:=null clears one.
--if-revision n refuses the change if someone changed the record since revision n. records schedule publishes or unpublishes later: a time is ISO 8601 with a time zone (2026-10-01T09:00:00Z), an offset from now (+30m, +2h, +3d, +1w), or none to clear one. records scheduled <collection> lists what is coming. A draft holds changes until it is merged, now (drafts merge) or at --merge-at; drafts create and update take the same data flags as records.

Fields and migrations

Field options are flags: --description, --options a,b (enum), --target <collection> (relation), --max-length n, --min n and --max n, --include-time and --time-zone <zone> (dates), --format slug|url|email and --unique (text), and --searchable, --filterable, --sortable (each with a --no- form). Field types accept rich-text or rich_text. fields migrate changes a field in a way that rewrites record values: its type, single or multiple, a stricter configuration, or required with a backfill. Run it with --dry-run first to see how every record converts. Without --no-wait it waits for the migration and exits non-zero if it fails or is cancelled. While it runs, the collection’s fields can’t change, and once it is running its records can’t either. Field migrations has the rules.

Schema as code, export and import

schema pull, schema diff and schema push keep a collection’s model in nohead.schema.yaml in your repository: Schema as code. export --output <dir> writes a project’s schema, records, drafts and asset metadata (--include-history, --include-deleted, and --include-files for the originals). import <dir> recreates them in another project: the schema first, then the records, relinking relations to the new IDs, then the drafts. Every request has an idempotency key derived from the export, so re-running an interrupted import within 24 hours doesn’t duplicate anything. Imported records start a new history.

Source

The CLI is open source, under the MIT license. Report issues and follow releases on GitHub:
nohead-io/nohead-cli
Loading repository data...