> ## Documentation Index
> Fetch the complete documentation index at: https://octolens.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up Octolens

> Go from a work email to a live trial from the terminal: your website, keywords, first mentions, feeds, Slack and your AI tools.

Flags and arguments below are the contract as the binary states it. The global flags (`--json`, `--timeout`, …) apply here too and are documented on [Global flags](/docs/cli/contract/global-flags). Error codes are a workspace-global contract, not a per-command one — the taxonomy, and how to read a code that is not in it, is on [The error envelope](/docs/cli/contract/error-envelope).

## `octolens onboard`

Set up Octolens for your company from the terminal: account, website, keywords, mentions, feeds, Slack and your AI tools. Under --json (or piped) it prints the setup's status envelope instead.

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--if-revision` | `string` | no | Refuse (exit 2, ONBOARDING\_REVISION\_CHANGED) unless the onboarding is still at this revision (the envelope's `revision`) |
| `--no-browser` | `boolean` | no | Never open a browser: print links instead |
| `--plain` | `boolean` | no | Append-only output: no in-place redraw, no animation (screen readers, logs) |
| `--wait` | `integer` | no | Seconds to wait for server work before answering (--json default 45, max 600; a TTY waits until done). A wait that runs out answers status "running" with a poll nextAction, exit 0. |
| `--yes` | `boolean` | no | Accept SAFE defaults without asking. Never removes a feed or writes MCP config on its own: those need their explicit flag too. |

**Examples**

```bash theme={null}
octolens onboard
octolens onboard --json
octolens onboard --plain
```

**Returns**

With `--json`, stdout carries one JSON document with these fields. Backed by the v2 API operation `GET /api/v2/onboarding`.

| Field | Type | Description |
| - | - | - |
| `schema` | `octolens.onboard/v1` | The protocol version. Branch on it before reading anything else. |
| `step` | enum: `account`, `website`, `keywords`, `mentions`, `feeds`, `slack`, `mcp`, `complete` | Where the setup is. |
| `position.index` | integer | `account` is 0, `mcp` and `complete` are 6. |
| `position.of` | integer | Always 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | Every status exits 0: the agent can act on it or wait it out. A refusal is never a status — it is a non-zero exit with an error envelope on stderr. |
| `engine` | enum: `sonar`, `classic` \| null | The setup engine pinned at verify; null before verify. |
| `revision` | string \| null | Pass it back as `--if-revision` to refuse a write if the setup moved on. |
| `handle.signupId` | string \| null | The pending signup between `onboard start` and `onboard verify`. |
| `handle.profile` | string \| null | The CLI profile holding the workspace key. |
| `handle.orgId` | string \| null | The workspace's organization id, once verified. |
| `handle.draftId` | string \| null | The setup draft (website scan + proposal). |
| `handle.proposalId` | string \| null | The keyword proposal. |
| `completed` | string\[] | Steps already done. |
| `data` | object | The step's data (website: `domain, scanSteps, profile`; keywords: `keywords[{ref, keyword, type}], slots, problems`; mentions: `yourMentions`, `relevant`, `noiseRemoved`, ≤20 relevant `posts`; feeds; slack; mcp: `endpoint` (the MCP v2 OAuth server), `auth`, `clients`, `client`, `connected` — no key is ever created for MCP). |
| `data.trial` | object | Once the workspace exists: `{plan, allowance, startsAt, endsAt}`. `startsAt` is `on_finish` and `endsAt` null until setup finishes — the 7-day clock starts at finish. |
| `data.alreadyDone` | true | The write had nothing to do (an idempotent re-run). |
| `data.reviewed` | true | Manual review approved the workspace (the post-review tail). |
| `needs` | object\[] | The flags the step still wants: `{flag, kind: decision\|human_value\|optional, default?, choices?, why}`. |
| `nextAction` | object \| null | What to do next; null exactly when `status` is `done`. |
| `nextAction.kind` | enum: `run`, `poll`, `decide`, `ask_human`, `open_url` | `run` = safe to execute unasked · `poll` = re-run after `retryAfterSeconds` · `decide` = relay `humanPrompt`, take `default` only if the human delegated · `ask_human` = fill the `<placeholder>` with a value only the person has · `open_url` = a person opens `url`, then poll `command`. A preference or destructive move is never `run`. |
| `nextAction.command` | string | The full command line, `octolens …`. |
| `nextAction.argv` | string\[] | The same command as argv (without the binary); it always ends with `--json`. |
| `nextAction.default` | boolean | For `decide`: the answer a delegating human takes. |
| `nextAction.humanPrompt` | string | What to relay to the person. |
| `nextAction.retryAfterSeconds` | integer \| null | For `poll`: when to re-run. |
| `nextAction.url` | string \| null | For `open_url`: the link the person opens. |
| `alternatives` | object\[] | Other legal moves: `{command, why}` (may carry `<placeholders>`). |
| `undo` | object \| null | Always null: nothing in the onboarding is undone from the CLI. |

Exactly one document on stdout and nothing on stderr. A refused request (a Sandbox workspace, a second trial, an existing account) prints NO stdout document: it exits non-zero with the error envelope on stderr, whose additive `nextAction` names the recovery (`octolens login` for EMAIL\_ALREADY\_REGISTERED, `onboard status` for RESPONSE\_LOST). The agent loop is `onboard status --json`, then run `nextAction.argv`.

Without `--json`: A terminal runs the guided setup, step by step, and prints where the setup is when it stops. Piped or under `--json` it does not prompt: it prints the status envelope, exactly like `onboard status`.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "keywords",
>   "position": {
>     "index": 2,
>     "of": 6
>   },
>   "status": "needs_input",
>   "engine": "sonar",
>   "revision": "rev_keywords_needs_input",
>   "handle": {
>     "signupId": null,
>     "profile": "default",
>     "orgId": "org_acme",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": "prop_7"
>   },
>   "completed": [
>     "account",
>     "website"
>   ],
>   "data": {
>     "archetype": "devtool",
>     "openingLine": "Acme makes feature flags.",
>     "keywords": [
>       {
>         "ref": "k1",
>         "keyword": "Acme",
>         "type": "own_brand"
>       },
>       {
>         "ref": "k2",
>         "keyword": "LaunchDarkly",
>         "type": "competitor"
>       }
>     ],
>     "slots": {
>       "used": 2,
>       "of": 5
>     },
>     "problems": []
>   },
>   "needs": [
>     {
>       "flag": "--confirm",
>       "kind": "decision",
>       "default": true,
>       "why": "Creates the trial workspace."
>     }
>   ],
>   "nextAction": {
>     "kind": "decide",
>     "command": "octolens onboard keywords --confirm --json",
>     "argv": [
>       "onboard",
>       "keywords",
>       "--confirm",
>       "--json"
>     ],
>     "retryAfterSeconds": null,
>     "url": null,
>     "default": true,
>     "humanPrompt": "Confirming creates your company's workspace with these 2 keywords. Your 7-day trial starts when setup finishes."
>   },
>   "alternatives": [
>     {
>       "command": "octolens onboard keywords --add <term> --json",
>       "why": "Add a keyword first."
>     },
>     {
>       "command": "octolens onboard keywords --remove <ref> --json",
>       "why": "Remove a keyword first."
>     },
>     {
>       "command": "octolens onboard keywords --rename <ref>=<term> --json",
>       "why": "Rename a keyword first."
>     }
>   ],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE · `8` CANCELLED. The full map is on [Exit codes](/docs/cli/contract/exit-codes).

## `octolens onboard feeds`

Step 4 of onboarding: review the feeds we made for you, rename or remove any, then go on to Slack

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--confirm` | `boolean` | no | Keep the feeds as they are now and go on to Slack |
| `--if-revision` | `string` | no | Refuse (exit 2, ONBOARDING\_REVISION\_CHANGED) unless the onboarding is still at this revision (the envelope's `revision`) |
| `--no-browser` | `boolean` | no | Never open a browser: print links instead |
| `--plain` | `boolean` | no | Append-only output: no colour, nothing redrawn (screen readers, logs) |
| `--remove` | `string` | no | Remove a feed by board number or id (repeatable; needs --yes) |
| `--rename` | `string` | no | Rename a feed: \<feed>=\<name>, where \<feed> is its board number or id (repeatable) |
| `--wait` | `integer` | no | Seconds to wait while your feeds are still being made (--json default 45, max 600). A wait that runs out answers status "running" with a poll nextAction, exit 0. |
| `--yes` | `boolean` | no | Confirm --remove. On its own it never removes a feed. |

**Examples**

```bash theme={null}
octolens onboard feeds
octolens onboard feeds --json
octolens onboard feeds --rename 2="Competitors" --confirm --json
octolens onboard feeds --remove 3 --yes --confirm --json
```

**Returns**

With `--json`, stdout carries one JSON document with these fields.

| Field | Type | Description |
| - | - | - |
| `schema` | string | Always `octolens.onboard/v1`. |
| `step` | enum: `account`, `website`, `keywords`, `mentions`, `feeds`, `slack`, `mcp`, `complete` | `feeds` while the board is open; after `--confirm`, the step the onboarding moved to (`slack`). |
| `position` | object | `{index, of}`: `feeds` is 4 of 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | `running` while your feeds are still being made (poll); `needs_input` at the board. |
| `engine` | enum: `sonar`, `classic` \| null | The onboarding engine. |
| `revision` | string \| null | The revision hash `--if-revision` takes. |
| `handle` | object | `{signupId, profile, orgId, draftId, proposalId}`. |
| `completed` | string\[] | The steps already done. |
| `data.state` | enum: `waiting`, `ready` | `waiting` until the noise removal has written the feeds. |
| `data.feeds` | object\[] | The board: `{n, id, name, kind, mentions, description}`. `n` is the board number `--rename` / `--remove` take (the id works too). |
| `needs` | object\[] | `--confirm` (a decision, default true) at the board. |
| `nextAction` | object \| null | At the board: `decide` → `onboard feeds --confirm --json`. While waiting: `poll` after `retryAfterSeconds`. After `--confirm`: the next step's action. |
| `alternatives` | object\[] | Rename / remove commands with `<feed>` placeholders. |
| `undo` | object \| null | Always null here: a removed feed is deleted. |

Order of writes: `--rename` (PATCH /api/v2/feeds/\{id}), then `--remove` (DELETE /api/v2/feeds/\{id}, only with `--yes`), then `--confirm` advances to Slack. `--remove` without `--yes` exits 2 `MISSING_REQUIRED_FLAGS` and deletes nothing; an empty name exits 2 (`Give the feed a name.`). Run before the mentions step is done it exits 2 `ONBOARDING_STEP_NOT_CURRENT`; run after the feeds were confirmed it writes nothing and `data.alreadyDone` is `true`.

Without `--json`: A terminal shows the numbered board (name, kind, mentions, what it shows) and one edit line: `rN` renames, `-N` marks a feed for removal, `u` undoes, Enter removes the marked feeds and goes on. Nothing is deleted before Enter; Ctrl-C exits 8.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "feeds",
>   "position": {
>     "index": 4,
>     "of": 6
>   },
>   "status": "needs_input",
>   "engine": "sonar",
>   "revision": "r_9f2c1a",
>   "handle": {
>     "signupId": null,
>     "profile": null,
>     "orgId": "org_2abcDEFghij345",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": "prop_7h2k"
>   },
>   "completed": [
>     "account",
>     "website",
>     "keywords",
>     "mentions"
>   ],
>   "data": {
>     "state": "ready",
>     "feeds": [
>       {
>         "n": 1,
>         "id": 311,
>         "name": "Brand mentions",
>         "kind": "brand",
>         "mentions": 41,
>         "description": "Everything people say about Acme Flags and Acme Experiments."
>       },
>       {
>         "n": 2,
>         "id": 312,
>         "name": "Competitor watch",
>         "kind": "competitors",
>         "mentions": 33,
>         "description": "LaunchDarkly and Statsig, so you see how you compare."
>       }
>     ],
>     "trial": {
>       "plan": "Free_trial",
>       "allowance": 5000,
>       "startsAt": "on_finish",
>       "endsAt": null
>     }
>   },
>   "needs": [
>     {
>       "flag": "--confirm",
>       "kind": "decision",
>       "default": true,
>       "why": "Keep the feeds as shown and go on to Slack."
>     }
>   ],
>   "nextAction": {
>     "kind": "decide",
>     "command": "octolens onboard feeds --confirm --json",
>     "argv": [
>       "onboard",
>       "feeds",
>       "--confirm",
>       "--json"
>     ],
>     "default": true,
>     "humanPrompt": "Keep these feeds? Brand mentions, Competitor watch",
>     "retryAfterSeconds": null,
>     "url": null
>   },
>   "alternatives": [
>     {
>       "command": "octolens onboard feeds --rename <feed>=<name> --json",
>       "why": "Rename a feed (board number or id)."
>     },
>     {
>       "command": "octolens onboard feeds --remove <feed> --yes --json",
>       "why": "Remove a feed (board number or id)."
>     }
>   ],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE · `8` CANCELLED. The full map is on [Exit codes](/docs/cli/contract/exit-codes).

## `octolens onboard finish`

Finish setting up Octolens: take the remaining steps, start your 7-day trial and print the receipt

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--if-revision` | `string` | no | Refuse the write unless the onboarding is still at this revision |
| `--no-browser` | `boolean` | no | Never open a browser: print links instead |
| `--plain` | `boolean` | no | Plain output for screen readers and logs: no colour |
| `--wait` | `integer` | no | Seconds to wait for server work before answering (--json default 45, max 600; a TTY waits until done). A wait that runs out answers status "running" with a poll nextAction, exit 0. |
| `--yes` | `boolean` | no | Accept SAFE defaults without asking. Never removes a feed or writes MCP config on its own: those need their explicit flag too. |

**Examples**

```bash theme={null}
octolens onboard finish
octolens onboard finish --json
octolens onboard finish --json | jq -r .data.trial.endsAt
```

**Returns**

With `--json`, stdout carries one JSON document with these fields.

| Field | Type | Description |
| - | - | - |
| `schema` | string | Always `octolens.onboard/v1`. |
| `step` | enum: `account`, `website`, `keywords`, `mentions`, `feeds`, `slack`, `mcp`, `complete` | Where the onboarding is AFTER this command. |
| `position.index` | integer | The step's position (`mcp` and `complete` are 6). |
| `position.of` | integer | Always 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | Every status exits 0; `done` once setup finished. |
| `engine` | enum: `sonar`, `classic` \| null | The onboarding engine. |
| `revision` | string \| null | The server's revision hash (`--if-revision`). |
| `handle.profile` | string \| null | The CLI profile in use. |
| `handle.orgId` | string \| null | The workspace's organization id. |
| `completed` | string\[] | The steps already done. |
| `data.trial` | object | `{plan, allowance, startsAt, endsAt}`; `endsAt` is set once setup finished (`onboardingFinishedAt` + 7 days). |
| `needs` | object\[] | The flags the step still wants, with `why`. |
| `nextAction` | object \| null | What to do next (`run`, `poll`, `decide`, …): `command`, `argv`, `retryAfterSeconds`, `url`. Null when done. |
| `alternatives` | object\[] | Other commands that apply. |
| `undo` | object \| null | Always null here. |

Walks the remaining legal transitions to `complete` (the last writes `onboardingFinishedAt`, which starts the trial). While step 3 is not done (the noise still being removed) it answers the `mentions` step with a `run` action instead (exit 0); before the workspace exists it exits 2 `ONBOARDING_STEP_NOT_CURRENT`. A finished setup answers `done` with `data.alreadyDone`.

Without `--json`: A terminal prints the receipt: keywords, feeds, AI tools, the trial's start and end dates, and up to three next commands.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "complete",
>   "position": {
>     "index": 6,
>     "of": 6
>   },
>   "status": "done",
>   "engine": "sonar",
>   "revision": "d71f02",
>   "handle": {
>     "signupId": null,
>     "profile": "acme",
>     "orgId": "org_2abcDEFghij345",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": "prop_7Hq2"
>   },
>   "completed": [
>     "account",
>     "website",
>     "keywords",
>     "mentions",
>     "feeds",
>     "slack",
>     "mcp"
>   ],
>   "data": {
>     "trial": {
>       "plan": "Free_trial",
>       "allowance": 5000,
>       "startsAt": "2026-10-19T09:20:41.000Z",
>       "endsAt": "2026-10-26T09:20:41.000Z"
>     }
>   },
>   "needs": [],
>   "nextAction": null,
>   "alternatives": [],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE. The full map is on [Exit codes](/docs/cli/contract/exit-codes).

## `octolens onboard keywords`

Step 2 of onboard: review the keywords Octolens suggests for you, change any, and create your workspace

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--add` | `string` | no | Add a keyword: term\[:type], type one of brand, competitor, industry (repeatable) |
| `--check` | `boolean` | no | Check the edited list against the server's rules; writes nothing |
| `--confirm` | `boolean` | no | Create the workspace with these keywords (the trial starts when setup finishes) |
| `--if-revision` | `string` | no | Refuse (exit 2, ONBOARDING\_REVISION\_CHANGED) unless the onboarding is still at this revision (the envelope's `revision`) |
| `--no-browser` | `boolean` | no | Never open a browser: print links instead |
| `--plain` | `boolean` | no | Append-only output: no in-place redraw, no animation (screen readers, logs) |
| `--remove` | `string` | no | Remove a keyword by its ref (k3), list number or text (repeatable) |
| `--rename` | `string` | no | Change a keyword: \<ref>=\<new keyword> (repeatable) |
| `--type` | `string` | no | Change a keyword's type: \<ref>=brand\|competitor\|industry (repeatable) |
| `--wait` | `integer` | no | Seconds to wait for the suggested keywords (--json default 45, max 600; a terminal waits until they are ready). A wait that runs out answers status "running" with a poll nextAction, exit 0. |
| `--yes` | `boolean` | no | Accept SAFE defaults without asking. Never removes a feed or writes MCP config on its own: those need their explicit flag too. |

**Examples**

```bash theme={null}
octolens onboard keywords
octolens onboard keywords --json
octolens onboard keywords --remove k3 --add 'growthbook:competitor' --check --json
octolens onboard keywords --rename k2='acme flags' --type k4=industry --confirm --json
octolens onboard keywords --confirm --json
```

**Returns**

With `--json`, stdout carries one JSON document with these fields. Backed by the v2 API operation `POST /api/v2/onboarding/trial`.

| Field | Type | Description |
| - | - | - |
| `schema` | `octolens.onboard/v1` | The protocol version. Branch on it before reading anything else. |
| `step` | enum: `account`, `website`, `keywords`, `mentions`, `feeds`, `slack`, `mcp`, `complete` | Where the setup is. |
| `position.index` | integer | `account` is 0, `mcp` and `complete` are 6. |
| `position.of` | integer | Always 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | Every status exits 0: the agent can act on it or wait it out. A refusal is never a status — it is a non-zero exit with an error envelope on stderr. |
| `engine` | enum: `sonar`, `classic` \| null | The setup engine pinned at verify; null before verify. |
| `revision` | string \| null | Pass it back as `--if-revision` to refuse a write if the setup moved on. |
| `handle.signupId` | string \| null | The pending signup between `onboard start` and `onboard verify`. |
| `handle.profile` | string \| null | The CLI profile holding the workspace key. |
| `handle.orgId` | string \| null | The workspace's organization id, once verified. |
| `handle.draftId` | string \| null | The setup draft (website scan + proposal). |
| `handle.proposalId` | string \| null | The keyword proposal. |
| `completed` | string\[] | Steps already done. |
| `data` | object | The step's data (website: `domain, scanSteps, profile`; keywords: `keywords[{ref, keyword, type}], slots, problems`; mentions: `yourMentions`, `relevant`, `noiseRemoved`, ≤20 relevant `posts`; feeds; slack; mcp: `endpoint` (the MCP v2 OAuth server), `auth`, `clients`, `client`, `connected` — no key is ever created for MCP). |
| `data.trial` | object | Once the workspace exists: `{plan, allowance, startsAt, endsAt}`. `startsAt` is `on_finish` and `endsAt` null until setup finishes — the 7-day clock starts at finish. |
| `data.alreadyDone` | true | The write had nothing to do (an idempotent re-run). |
| `data.reviewed` | true | Manual review approved the workspace (the post-review tail). |
| `needs` | object\[] | The flags the step still wants: `{flag, kind: decision\|human_value\|optional, default?, choices?, why}`. |
| `nextAction` | object \| null | What to do next; null exactly when `status` is `done`. |
| `nextAction.kind` | enum: `run`, `poll`, `decide`, `ask_human`, `open_url` | `run` = safe to execute unasked · `poll` = re-run after `retryAfterSeconds` · `decide` = relay `humanPrompt`, take `default` only if the human delegated · `ask_human` = fill the `<placeholder>` with a value only the person has · `open_url` = a person opens `url`, then poll `command`. A preference or destructive move is never `run`. |
| `nextAction.command` | string | The full command line, `octolens …`. |
| `nextAction.argv` | string\[] | The same command as argv (without the binary); it always ends with `--json`. |
| `nextAction.default` | boolean | For `decide`: the answer a delegating human takes. |
| `nextAction.humanPrompt` | string | What to relay to the person. |
| `nextAction.retryAfterSeconds` | integer \| null | For `poll`: when to re-run. |
| `nextAction.url` | string \| null | For `open_url`: the link the person opens. |
| `alternatives` | object\[] | Other legal moves: `{command, why}` (may carry `<placeholders>`). |
| `undo` | object \| null | Always null: nothing in the onboarding is undone from the CLI. |

Exactly one document on stdout and nothing on stderr. A refused request (a Sandbox workspace, a second trial, an existing account) prints NO stdout document: it exits non-zero with the error envelope on stderr, whose additive `nextAction` names the recovery (`octolens login` for EMAIL\_ALREADY\_REGISTERED, `onboard status` for RESPONSE\_LOST). The agent loop is `onboard status --json`, then run `nextAction.argv`. At the keywords step `data` carries the proposal (`state`, the keyword rows) and `data.trial` (`{plan, allowance, startsAt: "on_finish", endsAt: null}`). `--confirm` creates the workspace and starts the trial exactly once; a re-run answers `alreadyDone` with no second write. A bad flag value or a blocking problem exits 2 `KEYWORDS_STEP_INVALID`; a write before the website step is finished exits 2 `ONBOARDING_STEP_NOT_CURRENT`. Ctrl-C exits 8; only while the trial is being started does it print the interrupted-write warning (one JSON line on stderr under `--json`).

Without `--json`: A terminal shows the suggested keywords with their legend, lets you add, remove, rename or retype them at the `>` prompt, then creates your workspace and prints the next step.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "keywords",
>   "position": {
>     "index": 2,
>     "of": 6
>   },
>   "status": "needs_input",
>   "engine": "sonar",
>   "revision": "rev_keywords_needs_input",
>   "handle": {
>     "signupId": null,
>     "profile": "default",
>     "orgId": "org_acme",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": "prop_7"
>   },
>   "completed": [
>     "account",
>     "website"
>   ],
>   "data": {
>     "archetype": "devtool",
>     "openingLine": "Acme makes feature flags.",
>     "keywords": [
>       {
>         "ref": "k1",
>         "keyword": "Acme",
>         "type": "own_brand"
>       },
>       {
>         "ref": "k2",
>         "keyword": "LaunchDarkly",
>         "type": "competitor"
>       }
>     ],
>     "slots": {
>       "used": 2,
>       "of": 5
>     },
>     "problems": []
>   },
>   "needs": [
>     {
>       "flag": "--confirm",
>       "kind": "decision",
>       "default": true,
>       "why": "Creates the trial workspace."
>     }
>   ],
>   "nextAction": {
>     "kind": "decide",
>     "command": "octolens onboard keywords --confirm --json",
>     "argv": [
>       "onboard",
>       "keywords",
>       "--confirm",
>       "--json"
>     ],
>     "retryAfterSeconds": null,
>     "url": null,
>     "default": true,
>     "humanPrompt": "Confirming creates your company's workspace with these 2 keywords. Your 7-day trial starts when setup finishes."
>   },
>   "alternatives": [
>     {
>       "command": "octolens onboard keywords --add <term> --json",
>       "why": "Add a keyword first."
>     },
>     {
>       "command": "octolens onboard keywords --remove <ref> --json",
>       "why": "Remove a keyword first."
>     },
>     {
>       "command": "octolens onboard keywords --rename <ref>=<term> --json",
>       "why": "Rename a keyword first."
>     }
>   ],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE · `8` CANCELLED. The full map is on [Exit codes](/docs/cli/contract/exit-codes).

## `octolens onboard mcp`

Connect Claude Code, Cursor or Codex to Octolens's MCP server (it signs in to Octolens on first use), then finish setup (step 6 of octolens onboard)

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--client` | enum: `claude-code`, `cursor`, `codex`, `print` | no | The tool to connect: claude-code (runs `claude mcp add`), cursor (~~/.cursor/mcp.json), codex (~~/.codex/config.toml), or print (show the setup to paste) |
| `--if-revision` | `string` | no | Refuse the write unless the onboarding is still at this revision |
| `--no-browser` | `boolean` | no | Never open a browser: print links instead |
| `--plain` | `boolean` | no | Plain output for screen readers and logs: no colour, nothing redrawn |
| `--scope` | enum: `user`, `project` | no | Where the config goes: user (every project, the default) or project (this directory only; not for Codex) |
| `--skip` | `boolean` | no | Skip this step; connect a tool later with `octolens mcp connect` |
| `--wait` | `integer` | no | Seconds to wait for the tool's first Octolens call (with --json, after a tool was set up; max 600; a terminal waits until you press Enter) |
| `--yes` | `boolean` | no | Confirm writing the tool's MCP config without a prompt (required headless for claude-code, cursor, codex) |

**Examples**

```bash theme={null}
octolens onboard mcp
octolens onboard mcp --client claude-code --yes --json
octolens onboard mcp --client print --json
octolens onboard mcp --wait 45 --json
octolens onboard mcp --skip --json
```

**Returns**

With `--json`, stdout carries one JSON document with these fields.

| Field | Type | Description |
| - | - | - |
| `schema` | string | Always `octolens.onboard/v1`. |
| `step` | enum: `account`, `website`, `keywords`, `mentions`, `feeds`, `slack`, `mcp`, `complete` | Where the onboarding is AFTER this command. |
| `position.index` | integer | The step's position (`mcp` and `complete` are 6). |
| `position.of` | integer | Always 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | Every status exits 0; `done` once setup finished. |
| `engine` | enum: `sonar`, `classic` \| null | The onboarding engine. |
| `revision` | string \| null | The server's revision hash (`--if-revision`). |
| `handle.profile` | string \| null | The CLI profile in use. |
| `handle.orgId` | string \| null | The workspace's organization id. |
| `completed` | string\[] | The steps already done. |
| `data.trial` | object | `{plan, allowance, startsAt, endsAt}`; `endsAt` is set once setup finished (`onboardingFinishedAt` + 7 days). |
| `needs` | object\[] | The flags the step still wants, with `why`. |
| `nextAction` | object \| null | What to do next (`run`, `poll`, `decide`, …): `command`, `argv`, `retryAfterSeconds`, `url`. Null when done. |
| `alternatives` | object\[] | Other commands that apply. |
| `undo` | object \| null | Always null here. |
| `data.endpoint` | string | The MCP v2 server every writer targets (`<origin>/api/mcp/v2`). |
| `data.auth` | string | Always `oauth`: the tool signs in to Octolens on first use; no key is created. |
| `data.clients` | object\[] | `[{id, detected}]` for claude-code, cursor, codex, print. |
| `data.client` | string | The tool this run wrote the config for (or printed it for). Absent otherwise. |
| `data.connected` | boolean | Whether the tool made its first Octolens call. |
| `data.connectedAt` | string \| null | When it did. |

Writing a tool's config is a decision: `--client claude-code|cursor|codex` needs `--yes` headless (exit 2 `MISSING_REQUIRED_FLAGS`, nothing written); `--client print` writes no tool config: the snippet is in `data.snippet`. A failed write exits 9 `MCP_CONFIG_WRITE_FAILED`. The config holds the MCP v2 URL only (`/api/mcp/v2`): the tool signs in to Octolens with OAuth on first use, and no key is created. After a write, `onboard mcp --wait <s> --json` waits for the first call; a bare `onboard mcp --json` offers the choice again. Once the tool called (or with `--skip`) the step finishes setup and stdout carries the `done` envelope with the trial's end date.

Without `--json`: A terminal picks the tool, shows the command or file (the v2 URL only, no key) and how the tool signs in, asks `Run it? [Y/n]`, waits for the first question (Enter finishes) and prints the finish receipt.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "mcp",
>   "position": {
>     "index": 6,
>     "of": 6
>   },
>   "status": "needs_human",
>   "engine": "sonar",
>   "revision": "c63b1e",
>   "handle": {
>     "signupId": null,
>     "profile": "acme",
>     "orgId": "org_2abcDEFghij345",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": "prop_7Hq2"
>   },
>   "completed": [
>     "account",
>     "website",
>     "keywords",
>     "mentions",
>     "feeds",
>     "slack"
>   ],
>   "data": {
>     "endpoint": "https://app.octolens.com/api/mcp/v2",
>     "auth": "oauth",
>     "clients": [
>       {
>         "id": "claude-code",
>         "detected": true
>       },
>       {
>         "id": "cursor",
>         "detected": true
>       },
>       {
>         "id": "codex",
>         "detected": false
>       },
>       {
>         "id": "print",
>         "detected": true
>       }
>     ],
>     "client": "claude-code",
>     "connected": false,
>     "connectedAt": null,
>     "trial": {
>       "plan": "Free_trial",
>       "allowance": 5000,
>       "startsAt": "on_finish",
>       "endsAt": null
>     }
>   },
>   "needs": [
>     {
>       "flag": "--skip",
>       "kind": "optional",
>       "why": "Finish without waiting for the first call."
>     }
>   ],
>   "nextAction": {
>     "kind": "poll",
>     "command": "octolens onboard mcp --wait 45 --json",
>     "argv": [
>       "onboard",
>       "mcp",
>       "--wait",
>       "45",
>       "--json"
>     ],
>     "humanPrompt": "Ask your AI tool one question about your mentions (it asks you to sign in to Octolens the first time); the step completes on its first Octolens call.",
>     "retryAfterSeconds": 10,
>     "url": null
>   },
>   "alternatives": [
>     {
>       "command": "octolens onboard mcp --skip --json",
>       "why": "Skip; connect later with `octolens mcp connect`."
>     },
>     {
>       "command": "octolens onboard finish --json",
>       "why": "Finish setup now; the tool stays connected."
>     }
>   ],
>   "undo": null
> }
> ```

After `--skip`, or once the first call landed, stdout carries the finished setup's envelope (`step: complete`, `status: done`, `nextAction: null`) instead.

| Field | Type | Description |
| - | - | - |
| `step` | string | `complete`. |
| `status` | string | `done`. |
| `data.trial.endsAt` | string | The trial's end: `onboardingFinishedAt` + 7 days. |

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "complete",
>   "position": {
>     "index": 6,
>     "of": 6
>   },
>   "status": "done",
>   "engine": "sonar",
>   "revision": "d71f02",
>   "handle": {
>     "signupId": null,
>     "profile": "acme",
>     "orgId": "org_2abcDEFghij345",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": "prop_7Hq2"
>   },
>   "completed": [
>     "account",
>     "website",
>     "keywords",
>     "mentions",
>     "feeds",
>     "slack",
>     "mcp"
>   ],
>   "data": {
>     "trial": {
>       "plan": "Free_trial",
>       "allowance": 5000,
>       "startsAt": "2026-10-19T09:20:41.000Z",
>       "endsAt": "2026-10-26T09:20:41.000Z"
>     }
>   },
>   "needs": [],
>   "nextAction": null,
>   "alternatives": [],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE · `8` CANCELLED. The full map is on [Exit codes](/docs/cli/contract/exit-codes).

## `octolens onboard mentions`

See your first mentions: the relevant ones, with the noise removed for you (step 3 of octolens onboard)

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--if-revision` | `string` | no | Refuse the write unless the onboarding is still at this revision |
| `--limit` | `integer` | no | How many mentions to read (1-100) |
| `--no-browser` | `boolean` | no | Never open a browser: print links instead |
| `--offset` | `integer` | no | Read relevant mentions from this position (with --limit) |
| `--plain` | `boolean` | no | Plain output for screen readers and logs: no colour, nothing redrawn |
| `--wait` | `integer` | no | Seconds to wait for the collection and the noise removal before answering (default 45 with --json, max 600; a terminal waits until it is ready) |
| `--yes` | `boolean` | no | Continue without the Continue prompt |

**Examples**

```bash theme={null}
octolens onboard mentions
octolens onboard mentions --wait 45 --json
octolens onboard mentions --offset 20 --limit 100 --json
```

**Returns**

With `--json`, stdout carries one JSON document with these fields.

| Field | Type | Description |
| - | - | - |
| `schema` | string | Always `octolens.onboard/v1`. |
| `step` | enum: `account`, `website`, `keywords`, `mentions`, `feeds`, `slack`, `mcp`, `complete` | `mentions` (step 3), also once continued; a later step only on an idempotent re-run. |
| `position.index` | integer | The step's position (`mentions` is 3). |
| `position.of` | integer | Always 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | `running` while we collect, find the relevant mentions and remove the noise (poll), `pending_review` while the workspace waits for manual review; once continued, the next step's status. Every status exits 0. |
| `engine` | enum: `sonar`, `classic` \| null | The onboarding engine. |
| `revision` | string \| null | The server's revision hash (`--if-revision`). |
| `handle.profile` | string \| null | The CLI profile in use. |
| `handle.orgId` | string \| null | The workspace's organization id. |
| `handle.draftId` | string \| null | The setup the collection belongs to. |
| `completed` | string\[] | The steps already done (`mentions` once continued). |
| `data.stage` | enum: `collecting`, `reading`, `optimizing`, `ready` | How far the first collection got (`optimizing` = removing the noise). |
| `data.yourMentions` | integer | Your mentions: everything collected (relevant + noise). |
| `data.relevant` | integer | Relevant mentions: the ones about you. |
| `data.noiseRemoved` | integer \| null | Noise removed: what the automatic noise removal took out; null until step 3 is done, and on the classic path (nothing is removed there). |
| `data.removingNoise` | boolean | The list is ready but the noise removal is still landing (keep polling). |
| `data.posts` | object\[] | Up to 20 relevant mentions (platform, where/author, when, text; `--offset/--limit` reads others). |
| `data.trial` | object | `{plan, allowance, startsAt, endsAt}`; `endsAt` stays null until setup finishes. |
| `needs` | object\[] | Always empty: there is nothing to decide. |
| `nextAction` | object \| null | What to do next: `poll` re-run after `retryAfterSeconds` while the noise is removed; `run` `onboard mentions --json` once done (it continues); once continued, the next step's command. |
| `alternatives` | object\[] | Reading more relevant mentions (`--offset/--limit`). |
| `undo` | null | Always null. |

There is no decision: the server removes the noise on its own. The step waits up to `--wait` seconds for the collection and the noise removal, records the step as seen and continues. A wait that runs out is `running` with a `poll` action, never an error. The retired decision flags exit 2 `NOISE_REMOVED_AUTOMATICALLY`.

Without `--json`: A terminal shows the live collection and the noise removal, then Your mentions · Relevant mentions · Noise removed and the top relevant mentions, capped to the window, then Continue.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "mentions",
>   "position": {
>     "index": 3,
>     "of": 6
>   },
>   "status": "running",
>   "engine": "sonar",
>   "revision": "a41f9c",
>   "handle": {
>     "signupId": null,
>     "profile": "acme",
>     "orgId": "org_2abcDEFghij345",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": "prop_7Hq2"
>   },
>   "completed": [
>     "account",
>     "website",
>     "keywords"
>   ],
>   "data": {
>     "stage": "optimizing",
>     "progress": 92,
>     "found": 214,
>     "yourMentions": 214,
>     "relevant": 84,
>     "noiseRemoved": null,
>     "removingNoise": false,
>     "sources": [
>       {
>         "source": "reddit",
>         "label": "Reddit",
>         "count": 88,
>         "state": "done"
>       }
>     ],
>     "posts": [],
>     "trial": {
>       "plan": "Free_trial",
>       "allowance": 5000,
>       "startsAt": "on_finish",
>       "endsAt": null
>     }
>   },
>   "needs": [],
>   "nextAction": {
>     "kind": "poll",
>     "command": "octolens onboard mentions --wait 45 --json",
>     "argv": [
>       "onboard",
>       "mentions",
>       "--wait",
>       "45",
>       "--json"
>     ],
>     "humanPrompt": "Collecting your mentions and removing the noise.",
>     "retryAfterSeconds": 10,
>     "url": null
>   },
>   "alternatives": [],
>   "undo": null
> }
> ```

Once step 3 is done, the same document carries the three numbers and the relevant mentions, `completed` includes `mentions`, and `nextAction` is the next step's command (`feeds`).

| Field | Type | Description |
| - | - | - |
| `completed` | string\[] | Includes `mentions`. |
| `status` | string | The next step's status. |
| `nextAction` | object \| null | The next step's command. |

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "mentions",
>   "position": {
>     "index": 3,
>     "of": 6
>   },
>   "status": "needs_input",
>   "engine": "sonar",
>   "revision": "b52a0d",
>   "handle": {
>     "signupId": null,
>     "profile": "acme",
>     "orgId": "org_2abcDEFghij345",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": "prop_7Hq2"
>   },
>   "completed": [
>     "account",
>     "website",
>     "keywords",
>     "mentions"
>   ],
>   "data": {
>     "stage": "ready",
>     "progress": 100,
>     "found": 214,
>     "yourMentions": 214,
>     "relevant": 84,
>     "noiseRemoved": 130,
>     "removingNoise": false,
>     "sources": [
>       {
>         "source": "reddit",
>         "label": "Reddit",
>         "count": 88,
>         "state": "done"
>       }
>     ],
>     "posts": [
>       {
>         "id": 101,
>         "source": "twitter",
>         "keyword": "Acme Flags",
>         "place": {
>           "preposition": "by",
>           "name": "@mkdev"
>         },
>         "title": "Swapped LaunchDarkly for Acme Flags last week and the SDK is so much nicer",
>         "body": null,
>         "url": "https://x.com/mkdev/status/1",
>         "timestamp": "2026-10-19T07:00:00.000Z",
>         "aboutYou": true,
>         "giveaway": null,
>         "whyNot": null,
>         "sentimentLabel": "Positive",
>         "tags": []
>       }
>     ],
>     "trial": {
>       "plan": "Free_trial",
>       "allowance": 5000,
>       "startsAt": "on_finish",
>       "endsAt": null
>     }
>   },
>   "needs": [],
>   "nextAction": {
>     "kind": "decide",
>     "command": "octolens onboard feeds --json",
>     "argv": [
>       "onboard",
>       "feeds",
>       "--json"
>     ],
>     "default": true,
>     "retryAfterSeconds": null,
>     "url": null
>   },
>   "alternatives": [],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE · `8` CANCELLED. The full map is on [Exit codes](/docs/cli/contract/exit-codes).

## `octolens onboard slack`

Setup step 5: connect Slack through a one-time link, send each feed to a channel and post the first mentions (or --skip; the daily email stays on)

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--email-only` | `string` | no | Keep a feed out of Slack, in the daily email only: its number or id (repeatable) |
| `--if-revision` | `string` | no | Refuse (exit 2, ONBOARDING\_REVISION\_CHANGED) unless the onboarding is still at this revision (the envelope's `revision`) |
| `--no-browser` | `boolean` | no | Never open a browser: print links instead |
| `--no-deliver` | `boolean` | no | Save the routes without sending the first mentions now |
| `--plain` | `boolean` | no | Append-only output: no in-place redraw, no animation (screen readers, logs) |
| `--route` | `string` | no | Send a feed to a Slack channel: \<feed>=#\<channel>, where \<feed> is the feed's number or id (repeatable). Feeds you don't name keep the suggested channel. |
| `--skip` | `boolean` | no | Skip Slack. The daily email digest stays on; connect later with `octolens slack connect`. |
| `--wait` | `integer` | no | Seconds to wait for server work before answering (--json default 45, max 600; a TTY waits until done). A wait that runs out answers status "running" with a poll nextAction, exit 0. |
| `--yes` | `boolean` | no | Accept SAFE defaults without asking. Never removes a feed or writes MCP config on its own: those need their explicit flag too. |

**Examples**

```bash theme={null}
octolens onboard slack
octolens onboard slack --json
octolens onboard slack --wait 45 --json
octolens onboard slack --route 1=#mentions --route 2=#competitive-intel --email-only 3 --json
octolens onboard slack --skip --json
```

**Returns**

With `--json`, stdout carries one JSON document with these fields. Backed by the v2 API operation `PUT /api/v2/onboarding/slack/routes`.

| Field | Type | Description |
| - | - | - |
| `schema` | `octolens.onboard/v1` | The protocol version. Branch on it before reading anything else. |
| `step` | enum: `account`, `website`, `keywords`, `mentions`, `feeds`, `slack`, `mcp`, `complete` | Where the setup is. |
| `position.index` | integer | `account` is 0, `mcp` and `complete` are 6. |
| `position.of` | integer | Always 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | Every status exits 0: the agent can act on it or wait it out. A refusal is never a status — it is a non-zero exit with an error envelope on stderr. |
| `engine` | enum: `sonar`, `classic` \| null | The setup engine pinned at verify; null before verify. |
| `revision` | string \| null | Pass it back as `--if-revision` to refuse a write if the setup moved on. |
| `handle.signupId` | string \| null | The pending signup between `onboard start` and `onboard verify`. |
| `handle.profile` | string \| null | The CLI profile holding the workspace key. |
| `handle.orgId` | string \| null | The workspace's organization id, once verified. |
| `handle.draftId` | string \| null | The setup draft (website scan + proposal). |
| `handle.proposalId` | string \| null | The keyword proposal. |
| `completed` | string\[] | Steps already done. |
| `data` | object | The step's data (website: `domain, scanSteps, profile`; keywords: `keywords[{ref, keyword, type}], slots, problems`; mentions: `yourMentions`, `relevant`, `noiseRemoved`, ≤20 relevant `posts`; feeds; slack; mcp: `endpoint` (the MCP v2 OAuth server), `auth`, `clients`, `client`, `connected` — no key is ever created for MCP). |
| `data.trial` | object | Once the workspace exists: `{plan, allowance, startsAt, endsAt}`. `startsAt` is `on_finish` and `endsAt` null until setup finishes — the 7-day clock starts at finish. |
| `data.alreadyDone` | true | The write had nothing to do (an idempotent re-run). |
| `data.reviewed` | true | Manual review approved the workspace (the post-review tail). |
| `needs` | object\[] | The flags the step still wants: `{flag, kind: decision\|human_value\|optional, default?, choices?, why}`. |
| `nextAction` | object \| null | What to do next; null exactly when `status` is `done`. |
| `nextAction.kind` | enum: `run`, `poll`, `decide`, `ask_human`, `open_url` | `run` = safe to execute unasked · `poll` = re-run after `retryAfterSeconds` · `decide` = relay `humanPrompt`, take `default` only if the human delegated · `ask_human` = fill the `<placeholder>` with a value only the person has · `open_url` = a person opens `url`, then poll `command`. A preference or destructive move is never `run`. |
| `nextAction.command` | string | The full command line, `octolens …`. |
| `nextAction.argv` | string\[] | The same command as argv (without the binary); it always ends with `--json`. |
| `nextAction.default` | boolean | For `decide`: the answer a delegating human takes. |
| `nextAction.humanPrompt` | string | What to relay to the person. |
| `nextAction.retryAfterSeconds` | integer \| null | For `poll`: when to re-run. |
| `nextAction.url` | string \| null | For `open_url`: the link the person opens. |
| `alternatives` | object\[] | Other legal moves: `{command, why}` (may carry `<placeholders>`). |
| `undo` | object \| null | Always null: nothing in the onboarding is undone from the CLI. |

Exactly one document on stdout and nothing on stderr. A refused request (a Sandbox workspace, a second trial, an existing account) prints NO stdout document: it exits non-zero with the error envelope on stderr, whose additive `nextAction` names the recovery (`octolens login` for EMAIL\_ALREADY\_REGISTERED, `onboard status` for RESPONSE\_LOST). The agent loop is `onboard status --json`, then run `nextAction.argv`. At the Slack step with no live link it mints one and answers `open_url` with the URL in `nextAction.url` at once (nothing on stderr); with a live link it waits up to `--wait` for the install. A failed first delivery exits 9 (`SLACK_ROUTE_DELIVERY_FAILED` / `SLACK_ROUTE_DELIVERY_PARTIALLY_FAILED`) with the per-route `deliveries` in the error.

Without `--json`: A terminal asks to connect Slack, prints a one-time link and waits for it, lists where each feed goes (rN change channel, -N email only, u undo, Enter sends as shown) and prints one outcome line per route.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "keywords",
>   "position": {
>     "index": 2,
>     "of": 6
>   },
>   "status": "needs_input",
>   "engine": "sonar",
>   "revision": "rev_keywords_needs_input",
>   "handle": {
>     "signupId": null,
>     "profile": "default",
>     "orgId": "org_acme",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": "prop_7"
>   },
>   "completed": [
>     "account",
>     "website"
>   ],
>   "data": {
>     "archetype": "devtool",
>     "openingLine": "Acme makes feature flags.",
>     "keywords": [
>       {
>         "ref": "k1",
>         "keyword": "Acme",
>         "type": "own_brand"
>       },
>       {
>         "ref": "k2",
>         "keyword": "LaunchDarkly",
>         "type": "competitor"
>       }
>     ],
>     "slots": {
>       "used": 2,
>       "of": 5
>     },
>     "problems": []
>   },
>   "needs": [
>     {
>       "flag": "--confirm",
>       "kind": "decision",
>       "default": true,
>       "why": "Creates the trial workspace."
>     }
>   ],
>   "nextAction": {
>     "kind": "decide",
>     "command": "octolens onboard keywords --confirm --json",
>     "argv": [
>       "onboard",
>       "keywords",
>       "--confirm",
>       "--json"
>     ],
>     "retryAfterSeconds": null,
>     "url": null,
>     "default": true,
>     "humanPrompt": "Confirming creates your company's workspace with these 2 keywords. Your 7-day trial starts when setup finishes."
>   },
>   "alternatives": [
>     {
>       "command": "octolens onboard keywords --add <term> --json",
>       "why": "Add a keyword first."
>     },
>     {
>       "command": "octolens onboard keywords --remove <ref> --json",
>       "why": "Remove a keyword first."
>     },
>     {
>       "command": "octolens onboard keywords --rename <ref>=<term> --json",
>       "why": "Rename a keyword first."
>     }
>   ],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE · `8` CANCELLED. The full map is on [Exit codes](/docs/cli/contract/exit-codes).

## `octolens onboard start`

Start a free trial from the terminal: email a 6-digit code to your work email

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--email` | `string` | yes (headless) | Your work email; the 6-digit code is sent there (prompted for on a terminal) |
| `--heard-from` | `string` | no | How you heard about Octolens (e.g. linkedin, ai-tools:claude) |
| `--if-revision` | `string` | no | Refuse (exit 2, ONBOARDING\_REVISION\_CHANGED) unless the onboarding is still at this revision (the envelope's `revision`) |
| `--no-browser` | `boolean` | no | Never open a browser: print links instead |
| `--plain` | `boolean` | no | Plain output for screen readers and logs: no colour, nothing redrawn |
| `--ref` | `string` | no | Referral code, if someone sent you one |
| `--wait` | `integer` | no | Seconds to wait for server work before answering (--json default 45, max 600; a TTY waits until done). A wait that runs out answers status "running" with a poll nextAction, exit 0. |
| `--yes` | `boolean` | no | Accept SAFE defaults without asking. Never removes a feed or writes MCP config on its own: those need their explicit flag too. |

**Examples**

```bash theme={null}
octolens onboard start
octolens onboard start --email jane@acme.dev --json
octolens onboard start --email jane@acme.dev --heard-from ai-tools:claude --json
```

**Returns**

With `--json`, stdout carries one JSON document with these fields.

| Field | Type | Description |
| - | - | - |
| `schema` | string | Always `octolens.onboard/v1`. |
| `step` | enum: `account`, `website`, `keywords`, `mentions`, `feeds`, `slack`, `mcp`, `complete` | The step the onboarding is at AFTER this command. |
| `position.index` | integer | The step's position (`account` 0 … `mcp` 6). |
| `position.of` | integer | Always 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | What the step is waiting for. Every status exits 0; a refusal is an error envelope. |
| `engine` | enum: `sonar`, `classic` \| null | The onboarding engine pinned at verify; null before it. |
| `revision` | string \| null | The server's revision hash; null before verify. |
| `handle.signupId` | string \| null | The pending signup (`--signup-id` for `onboard verify`); null once verified. |
| `handle.profile` | string \| null | The CLI profile the key was saved under. |
| `handle.orgId` | string \| null | The new workspace's organization id. |
| `handle.draftId` | string \| null | The website-scan draft, once started. |
| `handle.proposalId` | string \| null | The keyword proposal, once made. |
| `completed` | string\[] | The steps already done. |
| `needs` | object\[] | The flags the step still wants: `{flag, kind: decision\|human_value\|optional, default?, choices?, why}`. |
| `nextAction` | object \| null | What to do next: `kind` (`run` safe to execute · `poll` re-run after `retryAfterSeconds` · `decide` relay `humanPrompt` · `ask_human` fill the `<placeholder>` in `argv` · `open_url`), `command`, `argv`, `humanPrompt?`, `retryAfterSeconds`, `url`. Null when done. |
| `alternatives` | object\[] | Other commands that apply here, `{command, why}`. |
| `undo` | object \| null | `{command, until}` when the last write can be undone. |
| `data.email` | string \| null | The work email the code was sent to. |
| `data.codeExpiresAt` | string \| null | ISO-8601 instant the emailed code stops working. |

`status` is `needs_human` and `nextAction.kind` is `ask_human`: only the person can read the emailed code. The pending signup is also saved to `pending-onboarding.json` (0600, no key) in the config dir, so `onboard verify` can omit `--signup-id` on the same machine. A personal email answers `WORK_EMAIL_REQUIRED` (exit 2). Re-running with the same email after a lost `verify` answer picks the same account back up.

Without `--json`: A terminal runs the whole account block: banner, `Work email:`, `Code:`, then the verified line. Ctrl-C exits 8 and keeps the pending code for the next run.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "account",
>   "position": {
>     "index": 0,
>     "of": 6
>   },
>   "status": "needs_human",
>   "engine": null,
>   "revision": null,
>   "handle": {
>     "signupId": "su_8Jq2",
>     "profile": null,
>     "orgId": null,
>     "draftId": null,
>     "proposalId": null
>   },
>   "completed": [],
>   "data": {
>     "email": "jane@acme.dev",
>     "codeExpiresAt": "2026-10-19T09:10:00.000Z"
>   },
>   "needs": [
>     {
>       "flag": "--code",
>       "kind": "human_value",
>       "why": "6-digit code emailed to jane@acme.dev"
>     }
>   ],
>   "nextAction": {
>     "kind": "ask_human",
>     "command": "octolens onboard verify --signup-id su_8Jq2 --code <code> --json",
>     "argv": [
>       "onboard",
>       "verify",
>       "--signup-id",
>       "su_8Jq2",
>       "--code",
>       "<code>",
>       "--json"
>     ],
>     "humanPrompt": "Paste the 6-digit code we emailed to jane@acme.dev",
>     "retryAfterSeconds": null,
>     "url": null
>   },
>   "alternatives": [
>     {
>       "command": "octolens onboard start --email jane@acme.dev --json",
>       "why": "Send a new code (the old one stops working)"
>     }
>   ],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE · `8` CANCELLED. The full map is on [Exit codes](/docs/cli/contract/exit-codes).

## `octolens onboard status`

Show where your Octolens setup is and the next step (the octolens.onboard/v1 envelope under --json)

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--plain` | `boolean` | no | Append-only output: no in-place redraw, no animation (screen readers, logs) |

**Examples**

```bash theme={null}
octolens onboard status
octolens onboard status --json
octolens onboard status --json | jq -r .nextAction.command
```

**Returns**

With `--json`, stdout carries one JSON document with these fields. Backed by the v2 API operation `GET /api/v2/onboarding`.

| Field | Type | Description |
| - | - | - |
| `schema` | `octolens.onboard/v1` | The protocol version. Branch on it before reading anything else. |
| `step` | enum: `account`, `website`, `keywords`, `mentions`, `feeds`, `slack`, `mcp`, `complete` | Where the setup is. |
| `position.index` | integer | `account` is 0, `mcp` and `complete` are 6. |
| `position.of` | integer | Always 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | Every status exits 0: the agent can act on it or wait it out. A refusal is never a status — it is a non-zero exit with an error envelope on stderr. |
| `engine` | enum: `sonar`, `classic` \| null | The setup engine pinned at verify; null before verify. |
| `revision` | string \| null | Pass it back as `--if-revision` to refuse a write if the setup moved on. |
| `handle.signupId` | string \| null | The pending signup between `onboard start` and `onboard verify`. |
| `handle.profile` | string \| null | The CLI profile holding the workspace key. |
| `handle.orgId` | string \| null | The workspace's organization id, once verified. |
| `handle.draftId` | string \| null | The setup draft (website scan + proposal). |
| `handle.proposalId` | string \| null | The keyword proposal. |
| `completed` | string\[] | Steps already done. |
| `data` | object | The step's data (website: `domain, scanSteps, profile`; keywords: `keywords[{ref, keyword, type}], slots, problems`; mentions: `yourMentions`, `relevant`, `noiseRemoved`, ≤20 relevant `posts`; feeds; slack; mcp: `endpoint` (the MCP v2 OAuth server), `auth`, `clients`, `client`, `connected` — no key is ever created for MCP). |
| `data.trial` | object | Once the workspace exists: `{plan, allowance, startsAt, endsAt}`. `startsAt` is `on_finish` and `endsAt` null until setup finishes — the 7-day clock starts at finish. |
| `data.alreadyDone` | true | The write had nothing to do (an idempotent re-run). |
| `data.reviewed` | true | Manual review approved the workspace (the post-review tail). |
| `needs` | object\[] | The flags the step still wants: `{flag, kind: decision\|human_value\|optional, default?, choices?, why}`. |
| `nextAction` | object \| null | What to do next; null exactly when `status` is `done`. |
| `nextAction.kind` | enum: `run`, `poll`, `decide`, `ask_human`, `open_url` | `run` = safe to execute unasked · `poll` = re-run after `retryAfterSeconds` · `decide` = relay `humanPrompt`, take `default` only if the human delegated · `ask_human` = fill the `<placeholder>` with a value only the person has · `open_url` = a person opens `url`, then poll `command`. A preference or destructive move is never `run`. |
| `nextAction.command` | string | The full command line, `octolens …`. |
| `nextAction.argv` | string\[] | The same command as argv (without the binary); it always ends with `--json`. |
| `nextAction.default` | boolean | For `decide`: the answer a delegating human takes. |
| `nextAction.humanPrompt` | string | What to relay to the person. |
| `nextAction.retryAfterSeconds` | integer \| null | For `poll`: when to re-run. |
| `nextAction.url` | string \| null | For `open_url`: the link the person opens. |
| `alternatives` | object\[] | Other legal moves: `{command, why}` (may carry `<placeholders>`). |
| `undo` | object \| null | Always null: nothing in the onboarding is undone from the CLI. |

Exactly one document on stdout and nothing on stderr. A refused request (a Sandbox workspace, a second trial, an existing account) prints NO stdout document: it exits non-zero with the error envelope on stderr, whose additive `nextAction` names the recovery (`octolens login` for EMAIL\_ALREADY\_REGISTERED, `onboard status` for RESPONSE\_LOST). The agent loop is `onboard status --json`, then run `nextAction.argv`.

Without `--json`: A terminal prints the step, its status, the trial and the next command in a few plain lines.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "keywords",
>   "position": {
>     "index": 2,
>     "of": 6
>   },
>   "status": "needs_input",
>   "engine": "sonar",
>   "revision": "rev_keywords_needs_input",
>   "handle": {
>     "signupId": null,
>     "profile": "default",
>     "orgId": "org_acme",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": "prop_7"
>   },
>   "completed": [
>     "account",
>     "website"
>   ],
>   "data": {
>     "archetype": "devtool",
>     "openingLine": "Acme makes feature flags.",
>     "keywords": [
>       {
>         "ref": "k1",
>         "keyword": "Acme",
>         "type": "own_brand"
>       },
>       {
>         "ref": "k2",
>         "keyword": "LaunchDarkly",
>         "type": "competitor"
>       }
>     ],
>     "slots": {
>       "used": 2,
>       "of": 5
>     },
>     "problems": []
>   },
>   "needs": [
>     {
>       "flag": "--confirm",
>       "kind": "decision",
>       "default": true,
>       "why": "Creates the trial workspace."
>     }
>   ],
>   "nextAction": {
>     "kind": "decide",
>     "command": "octolens onboard keywords --confirm --json",
>     "argv": [
>       "onboard",
>       "keywords",
>       "--confirm",
>       "--json"
>     ],
>     "retryAfterSeconds": null,
>     "url": null,
>     "default": true,
>     "humanPrompt": "Confirming creates your company's workspace with these 2 keywords. Your 7-day trial starts when setup finishes."
>   },
>   "alternatives": [
>     {
>       "command": "octolens onboard keywords --add <term> --json",
>       "why": "Add a keyword first."
>     },
>     {
>       "command": "octolens onboard keywords --remove <ref> --json",
>       "why": "Remove a keyword first."
>     },
>     {
>       "command": "octolens onboard keywords --rename <ref>=<term> --json",
>       "why": "Rename a keyword first."
>     }
>   ],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE. The full map is on [Exit codes](/docs/cli/contract/exit-codes).

## `octolens onboard verify`

Verify the emailed code: creates your account, saves the login and starts reading your website

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--code` | `string` | yes (headless) | The 6-digit code from the email (prompted for on a terminal) |
| `--domain` | `string` | no | Your company's website, when it isn't your email's domain (e.g. acme.dev) |
| `--heard-from` | `string` | no | How you heard about Octolens (e.g. linkedin, ai-tools:claude) |
| `--if-revision` | `string` | no | Refuse (exit 2, ONBOARDING\_REVISION\_CHANGED) unless the onboarding is still at this revision (the envelope's `revision`) |
| `--no-browser` | `boolean` | no | Never open a browser: print links instead |
| `--plain` | `boolean` | no | Plain output for screen readers and logs: no colour, nothing redrawn |
| `--profile` | `string` | no | Name to store this login under |
| `--signup-id` | `string` | no | The signupId `onboard start` printed (defaults to the pending signup saved on this machine) |
| `--wait` | `integer` | no | Seconds to wait for server work before answering (--json default 45, max 600; a TTY waits until done). A wait that runs out answers status "running" with a poll nextAction, exit 0. |
| `--yes` | `boolean` | no | Accept SAFE defaults without asking. Never removes a feed or writes MCP config on its own: those need their explicit flag too. |

**Examples**

```bash theme={null}
octolens onboard verify
octolens onboard verify --signup-id 4f2a9c81 --code 482913 --json
octolens onboard verify --signup-id 4f2a9c81 --code 482913 --domain acme.dev --json
```

**Returns**

With `--json`, stdout carries one JSON document with these fields.

| Field | Type | Description |
| - | - | - |
| `schema` | string | Always `octolens.onboard/v1`. |
| `step` | enum: `account`, `website`, `keywords`, `mentions`, `feeds`, `slack`, `mcp`, `complete` | The step the onboarding is at AFTER this command. |
| `position.index` | integer | The step's position (`account` 0 … `mcp` 6). |
| `position.of` | integer | Always 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | What the step is waiting for. Every status exits 0; a refusal is an error envelope. |
| `engine` | enum: `sonar`, `classic` \| null | The onboarding engine pinned at verify; null before it. |
| `revision` | string \| null | The server's revision hash; null before verify. |
| `handle.signupId` | string \| null | The pending signup (`--signup-id` for `onboard verify`); null once verified. |
| `handle.profile` | string \| null | The CLI profile the key was saved under. |
| `handle.orgId` | string \| null | The new workspace's organization id. |
| `handle.draftId` | string \| null | The website-scan draft, once started. |
| `handle.proposalId` | string \| null | The keyword proposal, once made. |
| `completed` | string\[] | The steps already done. |
| `needs` | object\[] | The flags the step still wants: `{flag, kind: decision\|human_value\|optional, default?, choices?, why}`. |
| `nextAction` | object \| null | What to do next: `kind` (`run` safe to execute · `poll` re-run after `retryAfterSeconds` · `decide` relay `humanPrompt` · `ask_human` fill the `<placeholder>` in `argv` · `open_url`), `command`, `argv`, `humanPrompt?`, `retryAfterSeconds`, `url`. Null when done. |
| `alternatives` | object\[] | Other commands that apply here, `{command, why}`. |
| `undo` | object \| null | `{command, until}` when the last write can be undone. |
| `data.domain` | string \| null | The domain whose website is being read. |
| `data.scanSteps` | object\[] | The scan's steps (empty right after verify). |
| `data.profile` | object \| null | The scanned profile (null until read). |

The document is the NEXT step (`website`): the account exists, the key is saved as the current profile (never printed) and the website scan has started, so `nextAction` polls `onboard website`. An email that already has an Octolens account answers `EMAIL_ALREADY_REGISTERED` (exit 2, run `octolens login`). A lost answer exits 10 `RESPONSE_LOST`: re-run `onboard start` with the same email, which re-adopts the account.

Without `--json`: A terminal prompts for a missing code, then prints the verified line.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "website",
>   "position": {
>     "index": 1,
>     "of": 6
>   },
>   "status": "running",
>   "engine": "sonar",
>   "revision": null,
>   "handle": {
>     "signupId": null,
>     "profile": "acme-dev",
>     "orgId": "org_2abcDEFghij345",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": null
>   },
>   "completed": [
>     "account"
>   ],
>   "data": {
>     "domain": "acme.dev",
>     "scanSteps": [],
>     "profile": null
>   },
>   "needs": [],
>   "nextAction": {
>     "kind": "poll",
>     "command": "octolens onboard website --wait 45 --json",
>     "argv": [
>       "onboard",
>       "website",
>       "--wait",
>       "45",
>       "--json"
>     ],
>     "retryAfterSeconds": 1,
>     "url": null
>   },
>   "alternatives": [],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE · `8` CANCELLED. The full map is on [Exit codes](/docs/cli/contract/exit-codes).

## `octolens onboard website`

Step 1 of onboard: watch Octolens read your website, correct the profile it found, and confirm it

**Flags**

| Flag | Type | Required (headless) | Description |
| - | - | - | - |
| `--add` | `string` | no | Add to a list row: competitor=, feature= or account=\<value> (repeatable) |
| `--confirm` | `boolean` | no | Accept the profile and move on to keywords |
| `--domain` | `string` | no | Read this website instead (starts a new read, about 20s) |
| `--heard-from` | `string` | no | How you heard about Octolens (e.g. linkedin, ai-tools:claude) |
| `--if-revision` | `string` | no | Refuse the edit unless the onboarding is still at this revision |
| `--no-browser` | `boolean` | no | Never open a browser: print links instead |
| `--plain` | `boolean` | no | Plain output for screen readers and logs: no colour, nothing redrawn |
| `--remove` | `string` | no | Remove from a list row: competitor=, feature= or account=\<value> (repeatable) |
| `--set` | `string` | no | Correct a text row: name=\<company name> or description=\<what you do> (repeatable) |
| `--wait` | `integer` | no | Seconds to wait for the read to finish (--json default 45, max 600; a terminal waits until it is done) |
| `--yes` | `boolean` | no | Accept SAFE defaults without asking. Never removes a feed or writes MCP config on its own: those need their explicit flag too. |

**Examples**

```bash theme={null}
octolens onboard website
octolens onboard website --wait 45 --json
octolens onboard website --remove competitor=Unleash --confirm --json
octolens onboard website --set name=Acme --add competitor=GrowthBook --add account=@acmedev --json
octolens onboard website --domain acme.dev --json
```

**Returns**

With `--json`, stdout carries one JSON document with these fields.

| Field | Type | Description |
| - | - | - |
| `schema` | string | Always `octolens.onboard/v1`. |
| `step` | enum: `website`, `keywords`, … | The step the onboarding is at AFTER this command: `website`, or `keywords` once confirmed. |
| `position.index` | integer | The step's position (`website` is 1). |
| `position.of` | integer | Always 6. |
| `status` | enum: `needs_input`, `running`, `needs_human`, `pending_review`, `done` | `running` while the website is read; `needs_input` once there is a profile to accept. |
| `engine` | enum: `sonar`, `classic` \| null | The onboarding engine pinned at verify. |
| `revision` | string \| null | The server's revision hash (`--if-revision`). |
| `handle.draftId` | string \| null | The website read the profile comes from. |
| `completed` | string\[] | The steps already done. |
| `data.domain` | string \| null | The website being read. |
| `data.scanSteps` | object\[] | One `{step, status: pending\|ok\|failed\|skipped, line, ms}` per thing the read collects. |
| `data.profile` | object \| null | What the read found: `name`, `description`, `productAreas[]`, `competitors[]`, `ownAccounts[]`, `protectedVocabulary[]` (Never skip), … Null until read, or when the read failed. |
| `needs` | object\[] | The flags the step still wants: `{flag, kind: decision\|human_value\|optional, default?, choices?, why}`. |
| `nextAction` | object \| null | `poll` while reading (re-run after `retryAfterSeconds`), `decide` to accept the profile (`humanPrompt` is the question to relay), `ask_human` when the read failed (the name and one-line description only a person can give). `command`, `argv`, `retryAfterSeconds`, `url`. |
| `alternatives` | object\[] | Other commands that apply here, `{command, why}`. |
| `undo` | object \| null | Always null for this step. |

Without write flags the command waits up to `--wait` seconds (45 by default) for the read, then answers `running` or the profile to accept. `--set/--add/--remove` edit the profile (only the fields that changed are sent; an over-cap edit exits 2 with the web editor's words) and `--confirm` accepts it: the document is then the keywords step. Re-running `--confirm` once the step moved on answers the current step with `data.alreadyDone: true`.

Without `--json`: A terminal with no flags runs the step: the live checklist of the read, the survey while it runs, the profile as a numbered form (a row number edits it), Enter to confirm. Ctrl-C exits 8.

> **Example output**
>
> ```json theme={null}
> {
>   "schema": "octolens.onboard/v1",
>   "step": "website",
>   "position": {
>     "index": 1,
>     "of": 6
>   },
>   "status": "needs_input",
>   "engine": "sonar",
>   "revision": "r_5b1e0c",
>   "handle": {
>     "signupId": null,
>     "profile": "acme",
>     "orgId": "org_2abcDEFghij345",
>     "draftId": "draft_k3v9x0q2m1",
>     "proposalId": null
>   },
>   "completed": [
>     "account"
>   ],
>   "data": {
>     "domain": "acme.dev",
>     "scanSteps": [
>       {
>         "step": "enrichment",
>         "status": "ok",
>         "line": "Researched Acme",
>         "ms": 3900
>       },
>       {
>         "step": "basic_info",
>         "status": "ok",
>         "line": "Found Acme and its accounts",
>         "ms": 5200
>       }
>     ],
>     "profile": {
>       "name": "Acme",
>       "description": "Open-source feature flags and experiments for product teams that ship every day.",
>       "industry": "feature flags",
>       "website": "acme.dev",
>       "domainVariants": [],
>       "productAreas": [
>         {
>           "label": "Acme Flags",
>           "path": "/flags"
>         }
>       ],
>       "ownAccounts": [
>         {
>           "platform": "x",
>           "handle": "acmedev"
>         }
>       ],
>       "founderHandles": [],
>       "protectedVocabulary": [
>         "Acme Flags"
>       ],
>       "competitors": [
>         {
>           "name": "LaunchDarkly",
>           "why": "Feature flag platform"
>         },
>         {
>           "name": "Statsig",
>           "why": "Experimentation platform"
>         }
>       ],
>       "relevanceContext": "",
>       "productUseCases": [],
>       "companyMoat": ""
>     }
>   },
>   "needs": [
>     {
>       "flag": "--confirm",
>       "kind": "decision",
>       "default": true,
>       "why": "Accept the profile: your keywords are built from it"
>     },
>     {
>       "flag": "--set",
>       "kind": "optional",
>       "why": "Correct a text row: name=… or description=…"
>     }
>   ],
>   "nextAction": {
>     "kind": "decide",
>     "command": "octolens onboard website --confirm --json",
>     "argv": [
>       "onboard",
>       "website",
>       "--confirm",
>       "--json"
>     ],
>     "default": true,
>     "humanPrompt": "Acme: feature flags. Competitors LaunchDarkly, Statsig. Correct?",
>     "retryAfterSeconds": null,
>     "url": null
>   },
>   "alternatives": [
>     {
>       "command": "octolens onboard website --remove competitor=<name> --confirm --json",
>       "why": "Drop a competitor, then accept"
>     }
>   ],
>   "undo": null
> }
> ```

**Exit codes** — `0` OK · `1` UNEXPECTED · `2` USAGE · `8` CANCELLED. The full map is on [Exit codes](/docs/cli/contract/exit-codes).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.