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:

FlagDescription
--jsonOutput as JSON (for AI agents and scripts)
--no-interactiveSkip all prompts, fail on missing values
--auto-confirmAuto-yes to confirmation prompts
--quietSuppress non-essential output
--api-key <key>Override API key from config
--api-url <url>Override API URL
--verboseShow debug output
-V, --versionShow version number
-h, --helpShow help

affitor init

Create a new affiliate program and generate config files.

npx affitor init
FlagDescriptionDefault
--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 days90
--duration-months <months>Recurring commission duration (0 = lifetime)12
--no-wizardSkip 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-interactive

Files created:

FilePurpose
.affitor/config.jsonProgram ID, API key, settings
.affitor/.env.exampleEnvironment variables template
AGENTS.mdTracking code snippets and AI agent instructions
skills.mdBackward-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 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.

FlagDescriptionDefault
--api-key <key>Program API key — overrides the env var / .affitor/.env(from config)
--yesAuto-confirm all diffs (apply every change without prompting)false
--jsonMachine-readable output for agents; never auto-edits filesfalse
--no-interactiveSkip prompts and apply changes without confirmationfalse

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)

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

The 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 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
FlagDescription
--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:

EventPurpose
customer.createdLead tracking
checkout.session.completedSale tracking
invoice.paidRecurring commission
invoice.payment_failedFailed payment alerts
charge.refundedAutomatic commission clawback
customer.subscription.deletedChurn 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 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
FlagDescription
--token <token>Polar Organization Access Token (or POLAR_ACCESS_TOKEN env; prompted otherwise)
--sandboxUse 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 --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 for the full recipe.


affitor status

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

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.

npx affitor test click    # Test click event
npx affitor test lead     # Test lead event
npx affitor test sale     # Test sale event
TypeWhat it tests
clickClick tracking pipeline
leadSignup/lead tracking
salePayment/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

ErrorMessage
No configNo Affitor config found. Run npx affitor init to set up your program.
Invalid API keyAPI key expired or invalid. Run npx affitor init to get a new one.
Already configuredAffitor already configured in this directory. Use npx affitor status to check.
Stripe OAuth cancelledStripe authorization cancelled. Run npx affitor setup stripe to try again.
Network errorNetwork error: <details>. Check your internet connection and try again.
© 2026 Affitor