# 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 Stripe sale call → 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 Stripe webhook handler and injects the `affitor.trackSale` call after the event is verified. 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 isn't Stripe, it degrades to **printing the exact snippets** for you to paste rather than guessing an edit site. Auto-edits to the Stripe 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.payment_succeeded` | Recurring commission |
| `invoice.payment_failed` | Failed payment alerts |
| `charge.refunded` | Automatic commission clawback |
| `customer.subscription.deleted` | Churn tracking |

---

## `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": 1,
  "program_id": "430",
  "domain": "example.com",
  "commission": {
    "type": "recurring_percent",
    "rate": 40,
    "duration_months": 12
  },
  "cookie": {
    "name": "_aff",
    "duration_days": 90
  },
  "stripe_connected": false,
  "api_key": "affitor_...",
  "api_url": "https://api.affitor.com"
}
```

---

## 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.` |
