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.
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):
npx affitor init \
--name "My SaaS" \
--domain example.com \
--commission-type recurring_percent \
--commission-rate 30 \
--duration-months 12 \
--no-interactiveFiles 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.
npx affitor onboardThis 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:
- 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).
- Browser tracking — installs
@affitor/sdkand wires the<AffitorTracker />component via a diff-preview, scaffoldinglib/affitor.ts(the same install wizardaffitor inituses). - Server sale — locates your payment webhook handler and injects the
affitor.trackSalecall after the event is verified: for Stripe, afterstripe.webhooks.constructEventin thecheckout.session.completedcase; for Polar, inside the@polar-sh/nextjsWebhooks({ onOrderPaid })callback (no webhook route yet? it points you ataffitor setup polar). It also persistsAFFITOR_API_KEYinto.env/.env.local(never overwriting an existing value). - Verify — fires the synthetic click → lead → sale chain through the real attribution pipeline, then polls program readiness until
integration_verifiedis 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)
npx affitor onboard --api-key affitor_xxx --yes --jsonThe JSON summary reports the per-step status and the final verdict:
{
"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.
npx affitor setup stripeWhat happens:
- Opens Stripe Connect OAuth in your browser
- You authorize Affitor to read your payment data
- Webhook endpoints are auto-created on your Stripe account
- 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_IDorAFFITOR_STRIPE_CLIENT_IDSTRIPE_SECRET_KEYorAFFITOR_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.
npx affitor setup polarWhat happens:
- A webhook endpoint is created on your Polar org (events
order.paid+order.refunded, formatraw) pointing at your app — defaulthttps://<your domain>/api/polar/webhook - The signing secret is saved to
.affitor/.env(gitignored) and written asPOLAR_WEBHOOK_SECRETinto your app's.env/.env.local - For Next.js (App Router), the glue route
app/api/polar/webhook/route.tsis generated — it validates the Standard-Webhooks signature via@polar-sh/nextjsand callstrackSale/trackRefundfrom@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:
npx affitor setup polar --token $POLAR_ACCESS_TOKEN --sandbox --jsonRenewals 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 for the full recipe.
affitor status
Check program health — tracking status, Stripe connection, and recent events.
npx affitor statusExample 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: 3affitor test [event-type]
Fire a test tracking event to confirm your integration is working end to end.
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:
{
"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. |