# CLI Command Reference

> Complete reference for all npx affitor commands, flags, and options.

Every `npx affitor` command, flag, and option in one place.

## Global Flags

Available on every command:

| Flag | Description |
|---|---|
| `--json` | Output as JSON (for AI agents and scripts) |
| `--no-interactive` | Skip all prompts, fail on missing values |
| `--auto-confirm` | Auto-yes to confirmation prompts |
| `--quiet` | Suppress non-essential output |
| `--api-key <key>` | Override API key from config |
| `--api-url <url>` | Override API URL |
| `--verbose` | Show debug output |
| `-V, --version` | Show version number |
| `-h, --help` | Show help |

---

## `affitor init`

Create a new affiliate program and generate config files.

```bash
npx affitor init
```

| Flag | Description | Default |
|---|---|---|
| `--name <name>` | Program name | (prompted) |
| `--domain <domain>` | Root domain | (prompted) |
| `--commission-type <type>` | `percent`, `fixed`, `recurring_percent`, `recurring_fixed` | (prompted) |
| `--commission-rate <rate>` | Commission rate (% or $) | 40 |
| `--cookie-duration <days>` | Cookie window in days | 90 |
| `--duration-months <months>` | Recurring commission duration (0 = lifetime) | 12 |
| `--no-wizard` | Skip the auto-install wizard and print manual setup steps | — |

**Example (non-interactive):**

```bash
npx affitor init \
  --name "My SaaS" \
  --domain example.com \
  --commission-type recurring_percent \
  --commission-rate 30 \
  --duration-months 12 \
  --no-interactive
```

**Files created:**

| File | Purpose |
|---|---|
| `.affitor/config.json` | Program ID, API key, settings |
| `.affitor/.env.example` | Environment variables template |
| `AGENTS.md` | Tracking code snippets and AI agent instructions |
| `skills.md` | Backward-compatible alias for `AGENTS.md` |

---

## `affitor onboard`

The recommended one-shot integration. Wires Affitor into this app end-to-end: detect the stack → install browser tracking → inject the sale call (Stripe or Polar) → verify. Run it from your project root after `affitor init`.

```bash
npx affitor onboard
```

This is the flagship path for AI coding agents: a single command that finds your framework and payment provider, applies the integration, and proves attribution works — instead of pasting snippets by hand.

| Flag | Description | Default |
|---|---|---|
| `--api-key <key>` | Program API key — overrides the env var / `.affitor/.env` | (from config) |
| `--yes` | Auto-confirm all diffs (apply every change without prompting) | `false` |
| `--json` | Machine-readable output for agents; never auto-edits files | `false` |
| `--no-interactive` | Skip prompts and apply changes without confirmation | `false` |

An API key is required. `onboard` resolves it from `--api-key`, the `AFFITOR_API_KEY` env var, or `.affitor/.env` (written by `affitor init`). If none is found it exits non-zero with `no_api_key`.

### What it does

`onboard` runs four phases in order:

1. **Detect** — inspects the project to identify the framework (Next.js app/pages router, Fastify, Express, plain Node) and the payment provider (Stripe, Polar, Lemon Squeezy, Paddle).
2. **Browser tracking** — installs `@affitor/sdk` and wires the `<AffitorTracker />` component via a diff-preview, scaffolding `lib/affitor.ts` (the same install wizard `affitor init` uses).
3. **Server sale** — locates your payment webhook handler and injects the `affitor.trackSale` call after the event is verified: for Stripe, after `stripe.webhooks.constructEvent` in the `checkout.session.completed` case; for Polar, inside the `@polar-sh/nextjs` `Webhooks({ onOrderPaid })` callback (no webhook route yet? it points you at `affitor setup polar`). It also persists `AFFITOR_API_KEY` into `.env` / `.env.local` (never overwriting an existing value).
4. **Verify** — fires the synthetic click → lead → sale chain through the real attribution pipeline, then polls program readiness until `integration_verified` is reached.

### Safety and idempotency

`onboard` is **idempotent** — re-running it skips any step already applied (an existing `AFFITOR_API_KEY`, a webhook that already reports the sale). It **never force-edits payment code it can't place confidently**: when the webhook shape isn't cleanly recognized, or no webhook is found, or the provider has no auto-edit path (Lemon Squeezy, Paddle), it degrades to **printing the exact snippets** for you to paste rather than guessing an edit site. Auto-edits to the payment handler always show a diff and ask for confirmation first (unless `--yes` / `--no-interactive`).

In `--json` mode `onboard` performs **no file edits** — it reports each step as `manual` so an agent drives the edits explicitly, then still fires the verification chain and polls readiness.

### Example (agent / non-interactive)

```bash
npx affitor onboard --api-key affitor_xxx --yes --json
```

The JSON summary reports the per-step status and the final verdict:

```json
{
  "program_id": "430",
  "steps": [
    { "step": "detect", "status": "ok", "detail": "framework=next-app, provider=stripe" },
    { "step": "browser_tracking", "status": "skipped", "detail": "json mode" },
    { "step": "server_sale", "status": "manual", "detail": "json mode (no auto-edit)" },
    { "step": "env_key", "status": "manual", "detail": ".env: json mode (no auto-edit)" }
  ],
  "integration_verified": true
}
```

If verification doesn't pass, the summary includes a `blocker` (the first failing readiness gate) and a `next_action` describing how to fix it. Re-run `affitor onboard` after resolving the blocker.

---

## `affitor setup stripe`

Connect your Stripe account so payments are tracked automatically.

```bash
npx affitor setup stripe
```

**What happens:**

1. Opens Stripe Connect OAuth in your browser
2. You authorize Affitor to read your payment data
3. Webhook endpoints are auto-created on your Stripe account
4. Connection is saved to your program config

| Flag | Description |
|---|---|
| `--stripe-client-id <id>` | Override Stripe Connect client ID |
| `--stripe-secret-key <key>` | Override Stripe secret key |

**Environment variables (alternative to flags):**

- `STRIPE_CONNECT_CLIENT_ID` or `AFFITOR_STRIPE_CLIENT_ID`
- `STRIPE_SECRET_KEY` or `AFFITOR_STRIPE_SECRET_KEY`

**Webhook events configured:**

| Event | Purpose |
|---|---|
| `customer.created` | Lead tracking |
| `checkout.session.completed` | Sale tracking |
| `invoice.paid` | Recurring commission |
| `invoice.payment_failed` | Failed payment alerts |
| `charge.refunded` | Automatic commission clawback |
| `customer.subscription.deleted` | Churn tracking |

The endpoint is created on your Stripe account pointing at Affitor's global webhook ingest route (`https://api.affitor.com/api/webhook-distributor/stripe`).

---

## `affitor setup polar`

Connect Polar (merchant of record — no Stripe account needed): creates the webhook endpoint on your Polar organization and generates the self-hosted glue route that reports every sale and refund to Affitor.

```bash
npx affitor setup polar
```

**What happens:**

1. A webhook endpoint is created on your Polar org (events `order.paid` + `order.refunded`, format `raw`) pointing at your app — default `https://<your domain>/api/polar/webhook`
2. The signing secret is saved to `.affitor/.env` (gitignored) and written as `POLAR_WEBHOOK_SECRET` into your app's `.env` / `.env.local`
3. For Next.js (App Router), the glue route `app/api/polar/webhook/route.ts` is generated — it validates the Standard-Webhooks signature via `@polar-sh/nextjs` and calls `trackSale` / `trackRefund` from `@affitor/sdk/server`

| Flag | Description |
|---|---|
| `--token <token>` | Polar Organization Access Token (or `POLAR_ACCESS_TOKEN` env; prompted otherwise) |
| `--sandbox` | Use the Polar sandbox environment (`sandbox-api.polar.sh` — sandbox tokens are separate) |
| `--url <url>` | Webhook delivery URL override |

The token needs the `webhooks:write` scope (Polar dashboard → Settings → Developers → New Token).

**Idempotent:** re-running reuses an endpoint that already delivers to the same URL (the signing secret is recovered from the Polar API), adds any missing events, and never overwrites an existing route file — run `affitor onboard` to inject the sale call into a route you wrote yourself.

**Agent mode:** with `--json` no app files are edited; the output includes the full route (`route.path`, `route.source`, `route.deps`, `route.env`) plus `next_actions` so an agent applies it explicitly:

```bash
npx affitor setup polar --token $POLAR_ACCESS_TOKEN --sandbox --json
```

Renewals need nothing extra — Polar fires `order.paid` on every billing cycle and checkout metadata (including a checkout link's `?reference_id=`) propagates to those orders. See the [Polar integration guide](/api-reference/integrations/polar) for the full recipe.

---

## `affitor status`

Check program health — tracking status, Stripe connection, and recent events.

```bash
npx affitor status
```

**Example output:**

```
  ╭──────────────────╮
  │   My SaaS        │
  │   example.com    │
  │                  │
  │   Program ID: 42 │
  ╰──────────────────╯

✓ Stripe: connected
⚠ DNS: not configured

  Events (last 24h):
    Clicks:  142
    Leads:   23
    Sales:   8

  Active partners:      5
  Pending commissions:  3
```

---

## `affitor test [event-type]`

Fire a test tracking event to confirm your integration is working end to end.

```bash
npx affitor test click    # Test click event
npx affitor test lead     # Test lead event
npx affitor test sale     # Test sale event
```

| Type | What it tests |
|---|---|
| `click` | Click tracking pipeline |
| `lead` | Signup/lead tracking |
| `sale` | Payment/sale tracking |

Test events are flagged `is_test: true` and shown in your dashboard with a test badge.

---

## Config File

Configuration is stored in `.affitor/config.json`:

```json
{
  "version": 2,
  "program_id": "430",
  "domain": "example.com",
  "commission": {
    "type": "recurring_percent",
    "rate": 40,
    "duration_months": 12
  },
  "cookie": {
    "name": "affitor_click_id",
    "duration_days": 90
  },
  "stripe_connected": false,
  "api_url": "https://api.affitor.com"
}
```

Secrets (the program API key, and provider webhook secrets) are **not** stored in `config.json` — they live in `.affitor/.env` (`AFFITOR_API_KEY`, `AFFITOR_PROGRAM_ID`), which the CLI adds to `.gitignore`. Legacy v1 configs that carried an inline `api_key` are migrated automatically on the next CLI run.

---

## Error Messages

| Error | Message |
|---|---|
| No config | `No Affitor config found. Run npx affitor init to set up your program.` |
| Invalid API key | `API key expired or invalid. Run npx affitor init to get a new one.` |
| Already configured | `Affitor already configured in this directory. Use npx affitor status to check.` |
| Stripe OAuth cancelled | `Stripe authorization cancelled. Run npx affitor setup stripe to try again.` |
| Network error | `Network error: <details>. Check your internet connection and try again.` |
