Command-line interface for the Notra API.
bun add -g notra
# or
npm i -g notraThe CLI runs on Node.js 22.12 or newer. Bun can also run it directly with
bunx --bun notra.
From your Notra Sites folder, run:
notra devOr run it without a global installation:
npx notra@latest dev
bunx --bun notra@latest devThe folder must contain blog.json, blog/, or changelog/; blog.json is
optional. The preview runs at http://localhost:4321 with live reload for
content, components, CSS, assets, and configuration. It includes drafts and
does not load analytics integrations. No login, API key, or deployment is
required. The first run downloads the pinned Sites renderer through npm when
running on Node, or through Bun when running on Bun. Subsequent runs reuse the
selected package manager's cache. Node users do not need Bun installed.
notra dev --source ./hosted-site --port 4400Both /blog and /changelog are served; / redirects to /blog. Stop with
Ctrl+C. dev prints server logs rather than JSON.
notra auth loginStarts a WorkOS Connect authorization-code flow with PKCE: the CLI opens
the sign-in and consent page in your browser, where you choose a workspace
and access level. A temporary callback listens only on 127.0.0.1 and
checks the login state before exchanging the code. Tokens are saved locally
with owner-only file permissions and refreshed automatically. No copy-pasting
tokens or embedding client secrets.
The CLI automatically registers and caches a public Connect client. No WorkOS
dashboard setup is needed when dynamic client registration is enabled.
Use notra auth login --no-browser to open the printed URL manually on the
same computer. For SSH/headless use, forward the printed callback port or
use an organization-scoped API key. The callback closes after ten minutes.
The hosted device-code page currently does not preserve the external-auth context with Notra's Standalone Connect login, so the CLI uses the same authorization-code flow as MCP clients rather than sending you to that page. Sessions from the old AuthKit device flow require signing in again.
Credential updates are serialized across CLI processes so parallel commands
cannot reuse the same rotating refresh token, and a delayed refresh cannot restore
credentials after logout.
notra auth logout only clears local credentials; it does not revoke server-side
access. Connect's current revocation endpoint requires a client secret even for
public clients, which the CLI must not embed.
notra posts list
notra posts get <postId>
notra posts create --title "Ship notes" --content-type changelog --markdown-file ./post.md
notra posts generate --content-type changelog --brand <id> --wait
notra posts schedule <postId> --scheduled-at 2027-01-01T08:00:00Z --time-zone Europe/Berlin
notra posts schedule-get <postId>
notra whoami
notra workspaces list
notra workspaces use <id|slug>
notra brands list
notra integrations list
notra schedules list
notra event-triggers list
notra webhooks endpoints list
notra webhooks deliveries list --status failed
notra chats list
notra chats create --message "What shipped this week?" --yes
notra skills list
notra feedback list --status new
notra agents list
notra geo projects list
notra geo snapshot <projectId> --days 30
notra geo visibility overview <projectId> --days 30
notra geo prompts create <projectId> --prompt "Which tools lead this category?"
notra api operations --tag GEO
notra api call getGeoSentiment --param projectId=project_123Run notra <topic> --help to see every command and flag. Every API command
accepts --json for machine-readable output.
GEO commands cover projects, settings, prompts, sequences, competitors, scans,
visibility, sentiment, scan changes, prompt history, citation shelf sources,
content gaps, briefs, agent readiness, and AI traffic. Run
notra geo --help to browse the complete command tree.
| Command group | Available workflows |
|---|---|
posts |
List, get, create, update, delete, generate, generation status; schedule, inspect or cancel publishing |
event-triggers |
List, get, create, replace configuration, delete |
webhooks endpoints |
List, create, delete subscriptions |
webhooks deliveries |
List, inspect payload and attempts, retry failed deliveries |
chats |
List, get, find by external channel, create, send messages or tool approvals |
skills |
List, get, create, update or rename, delete |
feedback |
List, get, update triage status, submit with a key or to a public organization URL |
agents |
List durable sessions, create, send messages or input responses, stream events |
whoami, workspaces list, workspaces use |
Inspect the current workspace, discover accepted or pending memberships, pick the workspace for an account API key |
Workspace discovery does not switch an OAuth session or an organization API key: those stay bound to one workspace. To use another workspace with them, sign in again for that workspace or pass its organization API key.
Account API keys (also ntra_…) belong to your user and work in every
workspace you are a member of. If you belong to more than one, choose the
workspace the CLI acts on:
notra workspaces list --api-key ntra_xxx
notra workspaces use acme # saves the workspace (ID or slug)
notra posts list --org acme # one-off override (ID or slug)
NOTRA_ORGANIZATION_ID=acme notra posts list
notra workspaces use --clear # forget the saved workspace--org (long form --organization-id) wins over NOTRA_ORGANIZATION_ID, which
wins over the saved organization-id config value. Each accepts a workspace ID
or slug. The CLI sends the choice as the X-Notra-Organization-Id header.
An organization key only accepts its own workspace there (any other gives a
403), and OAuth tokens ignore it.
New commands with JSON bodies accept individual field flags or --body-file with a JSON
object; field flags override values in that file. Use --body-file - for stdin.
Object and complex-array flags accept JSON. String-array flags such as --events
are repeatable. Boolean fields support --no-<flag> to send an explicit false.
notra event-triggers create --body-file ./trigger.json
notra webhooks endpoints create --url https://example.com/hooks \
--events post.created --events post.published
notra skills create --name humanizer --description "Write naturally" --content-file ./SKILL.md
notra skills update humanizer --new-name natural-writing
notra feedback submit-public acme --message "Search timed out" --kind bug
notra agents events <sessionId> --start-index 7 --timeout 300Webhook creation returns a signing secret only once. Store it securely. Post
publishing schedules are separate from recurring content-generation schedules.
Deleting resources or cancelling publishing requires confirmation; use --yes
in scripts. Creating chats, sending chat messages or tool approvals, and starting
or continuing durable agent sessions also require confirmation because they use
AI credits and their tools may act on data or external services.
Chat commands return { "chatId": "…", "text": "…" } after reading the reply
stream. When tools need approval, pendingApprovals includes their IDs, tool-call
IDs, and available tool inputs so you can inspect them before sending
--approvals '[{"id":"approval_123","approved":true}]' to chats message.
Automatic or already-answered approvals are not reported as pending.
Their default timeout is 300 seconds. agents events prints live NDJSON,
supports --start-index for replay, and stops after --timeout seconds (default
30). Each event is limited to 1 MiB of UTF-8 JSON; malformed or oversized events
fail the command rather than being returned as strings. Pending tool approvals
or questions can be answered with --body-file on
chats message or agents message.
posts generate --body-file accepts full source selection, including GitHub
repositories, data-point toggles, selected commits/PRs/releases/Linear issues,
and timezone. Existing generation flags override those fields. Integration
flags replace alternative selectors for the same source: --github-integration
replaces github.repositories and legacy repositoryIds, while
--linear-integration replaces legacy linearIntegrationIds. Conflicting
selectors in a JSON body without an override are rejected locally.
notra geo changes <projectId>
notra geo prompts history <projectId> <promptId> --scan-id <scanId>
notra geo visibility prompt-summaries <projectId> --limit 20 --mentioned false
notra geo visibility prompt-detail <projectId> <checkId>
notra geo sentiment get <projectId> --days 30
notra geo sentiment analysis <projectId> --days 30
notra geo sentiment evidence <projectId> --cursor <cursor>
notra geo shelf list <projectId> --offset 0 --limit 20Use compact prompt summaries before fetching full answers. geo snapshot
combines eight read-only signals into a bounded diagnosis with recommended next
actions. Optional sections have a five-second timeout and become warnings when
unavailable; a failed visibility overview still fails the command. Stored
sentiment analysis does not start a billed analysis run.
The curated commands above optimize common workflows. The api commands expose
the complete public OpenAPI surface without a generated SDK:
# Browse every bundled operation
notra api operations
notra api operations --search feedback
# Call an operation by operationId
notra api call getPost --param postId=post_123
notra api call createSkill --body-file ./skill.json
# Direct escape hatch for newly deployed endpoints
notra api request GET /v1/status
notra api request GET /v1/posts --query limit=20 --query status=draftPath, query, and header parameters use repeatable --param NAME=VALUE flags.
Request bodies come from --body-file; use - to read JSON from stdin. The raw
api request command remains available when an API deployment is newer than the
catalog bundled with the installed CLI.
Commands default to formatted output in a terminal and JSON when stdout is
redirected. Explicit output flags take precedence over that automatic choice;
for example, notra posts get <postId> --markdown always prints Markdown.
--json never adds tables, spinners, success text, or ANSI styling to stdout.
notra auth login --json streams newline-delimited JSON (NDJSON), with one
compact object per line. The pending event contains flow: "authorization_code",
authorizationUrl and expiresIn, followed by either a ready event or an
error event. JSON mode does not open the browser automatically.
The local config file lives at the OS-standard config path. Show it with:
notra config pathEnvironment overrides:
| Var | Default | Purpose |
|---|---|---|
NOTRA_API_KEY |
– | API key for requests (bypasses auth login) |
NOTRA_BASE_URL |
https://api.usenotra.com |
API base URL |
NOTRA_ORGANIZATION_ID |
– | Workspace ID or slug an account API key acts on (same as --org) |
NOTRA_WORKOS_CLIENT_ID |
automatically registered | Optional public Connect application client ID, not the WorkOS environment client ID; its redirect settings must permit the loopback callback |
NOTRA_OAUTH_ISSUER |
https://oauth.usenotra.com |
Connect authorization server for dev/staging; HTTPS required except for loopback test servers |
Or persist them:
notra config set api-key sk_live_xxx
notra config set base-url https://api.usenotra.com
notra config set organization-id acme| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Generic failure |
| 2 | Usage error (bad flag, missing required) |
| 3 | Auth failure (no key, 401, 403) |
| 4 | Rate-limited (429) |
| 5 | Not found (404, missing resource) |
| 6 | Network failure |
git clone https://github.com/usenotra/notra-cli && cd notra-cli
bun install
bun run dev -- posts list --help
bun run openapi:sync http://localhost:3000/openapi.json
bun run test
bun run typecheckSource is TypeScript with extensionless imports (moduleResolution: Bundler). A
small in-repo parser and dispatcher power the CLI; there is no generated SDK or
CLI framework in the runtime dependency graph. bun run dev executes the source
dispatcher. For distribution, bun run build bundles the command entrypoints
into dist; prepack runs that build automatically, and the published package
contains only dist.
The parser infers required, optional, repeatable, enum, integer, and boolean
types from each command definition. The shared HTTP client returns unknown
unless a response decoder is supplied. Commands with modeled responses use Zod
response schemas; other commands, including generic api calls, return the API
payload without pretending it has a compile-time type.
Effect is intentionally not a runtime dependency. The API backend benefits from Effect services, typed domain errors, and schedules; this short-lived CLI mostly performs one request and exits. A local Effect v4 experiment increased an isolated bundled Bun startup from 2.61 ms to 6.92 ms. Effect remains a reasonable future choice for a genuinely complex retry or concurrent workflow, but it does not replace decoding untrusted OpenAPI responses.
bun run openapi:sync [URL|FILE] refreshes the bundled operation catalog and
request schemas together in src/constants/openapi.ts, using one atomic file
replacement. It defaults to production and accepts a local API server URL or
saved OpenAPI JSON file. New curated API commands derive field flags and
validation from these schemas; no SDK is required. Domain-specific request rules
live in src/schemas/ and are supplied to the command factory via bodySchema,
rather than added as operation-ID branches in the shared command runner.
bun run test builds the package and tests every new API command against a local
mock server from both TypeScript/Bun and the packaged JavaScript/Node entrypoint.
It also checks help for every command, validation, authentication, confirmation,
stdin/file inputs, streaming, timeouts, API errors, and GEO snapshot fallbacks.
Tests do not modify production data or spend AI credits.