# CLI

> The checkbox command — connect a project with one approval, preflight changes, work the compliance plan, and use it in CI. Every command prints one JSON document with --json.

```bash
npx @checkbox-site/cli init
```

> **Pre-release packages**
>
> The SDKs, the CLI and the local MCP server are not on npm or PyPI yet, and the package names shown here are
> placeholders (the CLI commands below run from a checkout of the repository today). Until this notice is gone, use the
> HTTP API (curl) or the remote MCP server at `https://app.checkbox.site/mcp`, which are live today. Never install an npm
> package named `checkbox`: it belongs to an unrelated project.

The CLI is built for agents first: with `--json` every command writes exactly one JSON document to stdout (progress and
links go to stderr), includes a `next_action` when there is something to do, and uses stable exit codes. Without it,
output is short and human-readable.

## Credentials {#credentials}

The key is taken from `--api-key`, then `CHECKBOX_API_KEY`, then the key saved by `checkbox login` or `checkbox init`
(`~/.config/checkbox/config.json`, mode 0600; `CHECKBOX_CONFIG_DIR` overrides). The API base is `--api-base`, then
`CHECKBOX_API_BASE`, then `https://app.checkbox.site`. `checkbox whoami` shows which key is used and where it came from:

```bash
checkbox whoami
```

## Exit codes {#exit-codes}

| Code | Meaning |
|---|---|
| 0 | OK. For checks: everything passed, or only `needs_review` without `--strict` |
| 1 | A compliance check failed (`plan --exit-code`: not compliant yet; `verify`: failed) |
| 2 | A check needs review and `--strict` was given (`verify`: still pending) |
| 3 | Usage error |
| 4 | Not logged in, or the key was rejected |
| 5 | API, network or local error |

## Offline checks {#offline}

`checkbox check … --offline` runs the rules engine locally: no key, no network, the same JSON plus `"mode": "offline"`:

```bash
checkbox check email --kind acknowledgment --subject "Welcome to Joybox Pro" \
  --text "Thanks for subscribing to Joybox Pro. Your subscription renews automatically every month at \$19.99 until you cancel. Cancel anytime online at https://joybox.example/account/cancel." \
  --merchant-name Joybox --price 19.99 --interval month --cancel-url https://joybox.example/account/cancel --offline
```

## Commands {#commands}

| Command | What it does |
|---|---|
| [`checkbox answer`](#checkbox-answer) | Record the human's answer to a manual plan task (attestations need their confirmation link) |
| [`checkbox check`](#checkbox-check) | Preflight compliance checks: disclosure text, Stripe params, emails, checkout HTML |
| [`checkbox fix`](#checkbox-fix) | Show the change for a plan task (exact diff of your file for code tasks) |
| [`checkbox init`](#checkbox-init) | Add Checkbox to this project: one browser approval, then snippet, keys, policies and first checks |
| [`checkbox login`](#checkbox-login) | Save an API key for this machine (verified against the API) |
| [`checkbox logout`](#checkbox-logout) | Remove the API key saved by `checkbox login` |
| [`checkbox plan`](#checkbox-plan) | Show the compliance plan: ordered tasks to apply, then verify |
| [`checkbox verify`](#checkbox-verify) | Re-check a plan task (or the whole site) after applying a change |
| [`checkbox whoami`](#checkbox-whoami) | Show the account and mode the current API key acts on |

### checkbox answer {#checkbox-answer}

Record the human's answer to a manual plan task (attestations need their confirmation link)

```text
Usage: checkbox answer <task_id> <answer> [--note "<context from the human>"] [--json]

The answer must be one of the task's question.options (`checkbox plan` shows them): yes / no, confirmed /
needs_change, or done / not_now for steps only a person can do in the dashboard (e.g. connecting the billing
platform). "yes" and "confirmed" only count once the human confirms them: show them the link in next_action.
```

### checkbox check {#checkbox-check}

Preflight compliance checks: disclosure text, Stripe params, emails, checkout HTML

```text
Usage: checkbox check <disclosure|stripe-session|payment-link|email|page|notice-schedule> [flags]

Sub-commands:
  disclosure       Checkout disclosure text + placement + consent checkbox
  stripe-session   Params passed to stripe.checkout.sessions.create
  payment-link     Payment Link create params or a retrieved link object
  email            Lifecycle email (acknowledgment, reminders, price change, cancellation confirmation)
  page             Checkout page HTML (--file) or a live URL (--url, activated live accounts)
  notice-schedule  Exact dates each legally required notice must be sent (online only)

Plan flags (the subscription the checkout sells):
  --price <19.99> | --amount <1999>   recurring price (major units | minor units)
  --currency usd  --interval month|year|week|day  --interval-count 1  --trial-days 14
  --merchant-name "Joybox"  --plan-name "Pro"  --cancel-url https://…  --cancel-timing period_end|immediately

disclosure:     --text "…" | --file terms.txt   [--button-text Subscribe] [--no-above-button] [--no-same-page]
                [--font-size 14] [--contrast 7] [--bold] [--boxed] [--consent-checkbox] [--prechecked] [--consent-label "…"]
stripe-session: --file params.json (the object passed to stripe.checkout.sessions.create; - for stdin)
payment-link:   --file link.json (paymentLinks.create params, or a retrieved link incl. its id)
email:          --kind trial_reminder --subject "…" (--text "…" | --file email.txt|email.html) [--previous-price 14.99]
page:           --file checkout.html | --url https://… (url: online, activated live accounts) [--page-kind checkout|pricing]
notice-schedule: plan flags + [--start-date YYYY-MM-DD] [--price-change-date YYYY-MM-DD --new-price 24.99] [--cycles 2]

Common: --jurisdictions US-CA,US-NY (default all) · --as-of YYYY-MM-DD · --body request.json (send a full request body)
        --offline (run the rules engine locally; no key or network) · --strict (needs_review exits 2) · --verbose · --json

Exit: 0 pass (or needs_review without --strict) · 1 fail · 2 needs_review with --strict · 3 usage · 4 auth · 5 error

Examples:
  checkbox check disclosure --text "Renews monthly at $19.99 until you cancel." --price 19.99 --interval month --consent-checkbox
  checkbox check stripe-session --file session.json --merchant-name Joybox --cancel-url https://joybox.com/cancel --json
  checkbox check email --kind acknowledgment --file emails/welcome.html --price 19.99 --interval month --offline
```

### checkbox fix {#checkbox-fix}

Show the change for a plan task (exact diff of your file for code tasks)

```text
Usage: checkbox fix <task_id> [--file <path>] [--out <patch file>] [--json]

Prints the task's change. For code tasks it reads --file (default: the task's file_hint when it exists in this
directory), applies the task's anchored edits in memory and prints an exact unified diff — it never edits your files.

Flags:
  --file <path>   Source file that renders the page (component, template, HTML)
  --out <path>    Also write the diff to this file (then: git apply <path>)
  --json          { object: "task_fix", diff, applied, unmatched, content, explanation, verify, next_action }
```

### checkbox init {#checkbox-init}

Add Checkbox to this project: one browser approval, then snippet, keys, policies and first checks

```text
Usage: checkbox init [--yes] [--json] [--site-url <url>] [--terms-url <url>] [--dry-run] [--verify] [--complete]

Run it in the project root. What it does (idempotent; re-run any time):
  1. detects the framework, billing platform, checkout code and pay buttons
  2. signs you in: prints ONE link ("Open … and click Approve") and opens the browser; new accounts are free
  3. writes CHECKBOX_SECRET_KEY and the publishable key to .env.local / .env (and git-ignores it)
  4. installs the Checkbox snippet in the root layout (marked with checkbox:snippet comments)
  5. registers the site, Terms / refund / cancellation policy URLs (fetched and checked by Checkbox)
  6. with your permission, uses the project's Stripe TEST key for a synthetic test subscription
  7. runs the first checks and prints the next step (`checkbox plan --json`); the human gets the summary email
     when the setup is finished (every check passes, or `checkbox init --complete`)

Flags (agents: use --yes --json):
  --yes, -y               No questions; apply every change
  --json                  One JSON document on stdout (progress and the login link on stderr)
  --site-url <url>        Public URL of the site (default: detected from env / package.json)
  --checkout-url <url>    Pricing or checkout page
  --terms-url, --privacy-url, --refund-url, --cancellation-url <url>   Policy pages (default: found in the site footer)
  --account-url <url>     Account page where subscribers cancel
  --merchant-name <name>  Brand named as the seller in generated legal copy (default: detected <title> / package name)
  --plan <spec>           Plan terms, e.g. "19.99/month,trial=14" (several: separate with ;). Default: read from
                          inline price_data in your Checkout Session code
  --support-email <addr>  Support address for subscribers
  --dry-run               Show the planned file changes; write nothing
  --no-snippet            Do not touch the layout (print the tag instead)
  --no-stripe             Do not offer the Stripe test key / test subscription
  --live                  Also request live-mode keys and write the domain-verification file
  --no-wait               Start the login, print the link and exit (run `checkbox init` again to continue)
  --wait-seconds <n>      How long to wait for the approval (default 900 on a terminal, 100 otherwise)
  --no-open               Do not open the browser
  --verify                Re-check the site now (after deploying) and print the updated checklist
  --complete              Finish the setup and email the human the summary (sent automatically the first time every
                          check passes; run this when you stop earlier)
  --relogin               Ignore the saved login and approve again
  --agent <name>          Name of the AI agent shown on the approval page (auto-detected for Claude Code, Cursor…)
```

### checkbox login {#checkbox-login}

Save an API key for this machine (verified against the API)

```text
Usage: checkbox login [--api-key <key> | <key> | --stdin] [--no-verify]

Flags:
  --api-key <key>   The key (ck_test_…, ck_live_…, rk_…, sk_live_…). Also accepted as the first argument.
  --stdin           Read the key from stdin (echo $KEY | checkbox login --stdin)
  --no-verify       Save without calling the API

Without a key on an interactive terminal, you are prompted for one. Keys: https://app.checkbox.site/settings/api-keys
No account yet? `checkbox init` creates one with a single browser approval.
In CI, set CHECKBOX_API_KEY instead of logging in.
```

### checkbox logout {#checkbox-logout}

Remove the API key saved by `checkbox login`

```text
Usage: checkbox logout
```

### checkbox plan {#checkbox-plan}

Show the compliance plan: ordered tasks to apply, then verify

```text
Usage: checkbox plan [--site <site_id>] [--all] [--exit-code] [--json]

Flags:
  --site <id>    Site (default: $CHECKBOX_SITE, the site `checkbox init` created for this project, or your only site)
  --all          Also list done and declined tasks
  --exit-code    Exit 1 while the plan is not compliant (for CI)
  --json         The plan object exactly as the API returns it (for agents)

The loop: apply plan.next_action → `checkbox fix <task>` (prints the change, or an exact diff of your file) →
`checkbox verify <task> --wait` → `checkbox plan` again, until status is compliant. Tasks marked [!] need a human
answer (answerTask); they never block the others.
```

### checkbox verify {#checkbox-verify}

Re-check a plan task (or the whole site) after applying a change

```text
Usage: checkbox verify [task_id] [--wait] [--timeout <s>] [--html-file <f>] [--stripe-params <f>] [--email-file <f>] [--site <id>] [--json]

With a task id: runs the fastest re-check for the task (static fetch of your live checkout, a re-fetch of your Terms,
the Stripe params or email copy you pass) and queues the confirming scan / sync. Without: re-checks the whole site.

Flags:
  --wait              Wait until the verification passes or fails (long-poll)
  --timeout <s>       Give up waiting after this many seconds (default 600)
  --html-file <f>     Pre-check this rendered HTML before deploying (page tasks)
  --stripe-params <f> JSON your code now passes to stripe.checkout.sessions.create (Stripe tasks)
  --email-file <f>    JSON { subject, text | html, kind? } of the new email template (email tasks)

Exit codes: 0 passed · 1 failed · 2 still pending (not waited, or timed out) · 3 usage · 4 auth · 5 error
```

### checkbox whoami {#checkbox-whoami}

Show the account and mode the current API key acts on

```text
Usage: checkbox whoami
```
