AffiliateTracking docs
The AffiliateTracking docs on one page: how to start on your own server, connect your site and billing, and use the API. Pick where you are.
Eight places to start. Each one jumps to its section below.
Start on your own server
Self-host is one file with SQLite built in. Run it, make an admin key, and tell it which domains links may send people to.
Flags go before the command. Want Postgres instead? Pass a postgres:// address to -db and run migrate first. The full self-host guide covers servers, backups and proxies.
# 1. Start it (creates the database) ./affiliate-engine -listen :8080 -db sqlite:affiliate-engine.db # 2. Make an admin key. It's shown once ./affiliate-engine -db sqlite:affiliate-engine.db \ keygen -scope admin -name ops # 3. Where links may go, and your address AE_ALLOWED_DEST="yoursite.com" AE_PUBLIC_URL="https://track.yoursite.com" # Health check curl https://track.yoursite.com/healthz
Connect your site
Tracking links record the click. The script keeps the token on your domain until checkout.
Tracking links
Every affiliate link goes through /r/ and lands on an allowed page with a signed token.
https://yourbrand.affiliatetracking.co/r/jane ?dest=https://yoursite.com/pricing &subid=newsletter&utm_source=youtube
subid, utm_source and placement get their own columns in the affiliate's stats.
The script, t.js
It saves the token in a first-party cookie called at_token, then removes it from the address bar. The cookie lasts your attribution window: 30 days by default, 1 to 365.
// In the browser AffiliateTracking.token() // the raw token AffiliateTracking.clientReferenceId() // at1_... for Stripe
Moving from another tool? data-param="via" (or fpr, or ref) keeps old links working.
Connect billing
Each billing tool sends events to its own address. Each has a full setup guide.
| Billing tool | Webhook address | Self-host settings | Setup guide |
|---|---|---|---|
| Stripe | /webhooks/stripe | AE_STRIPE_SECRET (whsec_) | Stripe guide |
| Paddle | /webhooks/paddle | AE_PADDLE_SECRET | Paddle guide |
| Lemon Squeezy | /webhooks/lemonsqueezy | AE_LEMONSQUEEZY_SECRET | Lemon Squeezy guide |
| Polar | /webhooks/polar | AE_POLAR_SECRET | Webhook events order.paid and order.refunded |
| Chargebee | /webhooks/chargebee | AE_CHARGEBEE_USERNAME, AE_CHARGEBEE_PASSWORD | Chargebee guide |
| Recurly | /webhooks/recurly | AE_RECURLY_USERNAME, AE_RECURLY_PASSWORD, AE_RECURLY_API_KEY | Recurly guide |
REST API
67 routes: 46 for admins, 11 for affiliates and 10 public. Everything is JSON under /api/v1.
Keys and limits
Send a key as a bearer token. Admin keys see everything. Affiliate keys see one affiliate. Keys start ae_, and we only store a hash.
curl https://yourbrand.affiliatetracking.co/api/v1/admin/dashboard \ -H "Authorization: Bearer ae_..."
Limits: 600 requests a minute per key. After 30 failed tries a minute from one IP, it answers 429.
Affiliate and public routes
GET /affiliate/earningsTotals, 30-day stats and the ledger proofGET /affiliate/ledgerJournals, page by pageGET /affiliate/payoutsTheir payouts: amount, method, paid or on its wayGET,PUT /affiliate/payout-detailsWhere they want to be paid (PayPal, Wise or bank; stored encrypted)GET /affiliate/resourcesBanners, logos, text and links from the programGET /affiliate/stats/drillBy subid, utm_source or placementGET /affiliate/uniquesDaily unique visitorsGET /affiliate/profileLevel and badgesPOST /portal/applyApply to a programPOST /portal/login,/portal/logoutPortal sessionsPOST /portal/login-linkEmail an affiliate a one-time sign-in linkGET,POST /portal/unsubscribeStop program emails: news, tips and recaps (link in each email)GET /leaderboard,/portal/brand,/portal/logoPublic program info and logoGET /portal/resources/{id}/imageA banner or logo from the program, for partners' sites
Admin routes, under /api/v1/admin
GET dashboardGET, POST campaignsGET, POST rulesGET, POST, DELETE tiersGET, POST, DELETE resourcesPOST resources/imageGET, PUT brandingPUT, DELETE branding/logoPATCH campaigns/{id}GET, PUT settings
GET affiliatesPATCH affiliates/{id}PUT affiliates/{id}/rateGET, POST, DELETE codesGET portal/applicationsPOST portal/review
GET fraud/queuePOST fraud/resolvePOST payouts/runPOST payouts/approveGET, POST taxdocsGET payoutsGET payouts/export/{paypal,wise}.csv
POST usagePOST mlm/runPOST mlm/placePOST importGET import/batchesPOST import/undoGET setup-checkPOST setup-check/sitePOST setup-check/start
Command line
Self-host only. The pattern is affiliate-engine [-db address] [-listen addr] command. With no command, it serves.
| Command | What it does |
|---|---|
keygen -scope admin -name ops | Makes an API key. Use -scope affiliate -affiliate 42 for one affiliate. |
doctor | Runs 6 integrity checks. Exits 1 if the money doesn't add up. SQLite. |
backup [out.db] | Copies the database while it runs. SQLite. |
restore -from backup.db | Checks the backup, then swaps it in. SQLite. |
migrate | Sets up or updates the database. Run it first on Postgres. |
import -from rewardful -csv f.csv --dry-run | Imports affiliates, codes and coupons. Also firstpromoter, tapfiliate or tolt. -list shows batches and -undo ID undoes one. The admin screen does the same. |
compare | Puts your old tool's latest export next to this install's data for a day or range: clicks, new customers and commission, with the difference. |
screen-backfill -dry-run | Runs the fraud rules over sales that releases before v0.5.0 never screened. Drop -dry-run to hold what it finds. Safe while the server runs. |
test-conversion -affiliate 42 | Pushes a test sale through the real engine, fraud checks included. |
gdpr-export, gdpr-erase | Exports or erases one affiliate's data. The books still balance. |
export-parquet -out dir | Writes your data as Parquet files for analysis. |
bench, version, help | Click benchmark, version and help. |
Settings
Self-host reads these environment variables. On cloud, we set the core ones for you.
| Variable | What it sets | Default |
|---|---|---|
| Core | ||
AE_ALLOWED_DEST | Domains links may send people to, separated by ; | example.com |
AE_PUBLIC_URL | Your public address. https also makes cookies Secure | From the request |
AE_MAIL_PROVIDER | postmark or resend turns on partner emails: sign-in links and payout notes. Needs AE_PUBLIC_URL | Off |
AE_MAIL_KEY | Your Postmark server token or Resend API key | None |
AE_MAIL_FROM | An address your provider has verified. Emails show your program name in front of it | None |
AE_MAIL_REPLY_TO | Where partner replies go | The From address |
AE_OWNER_EMAIL | Where your "something needs you" email goes (change it in Settings) | Your reply-to address |
AE_TOKEN_SECRET | Signs tokens. At least 32 bytes | Made and saved on first start |
AE_SECRET_FILE | Where it keeps that generated secret | Next to the database |
AE_TRUSTED_PROXIES | Proxies allowed to pass the visitor's IP and country | None |
AE_COOKIE_SECURE | 1 sets Secure on every cookie | Off |
AE_REDIRECT_RATE_PER_MIN | Link clicks per IP per minute. -1 turns it off | 120 |
AE_MIGRATE_DATABASE_URL | Owner address for migrations, for a two-role Postgres setup | Unset |
| Stripe sales filter | ||
AE_STRIPE_PRODUCTS | Product, price or Payment Link IDs that earn commission | Every sale counts |
AE_STRIPE_API_KEY | Restricted key with Checkout Sessions read, for one-time checkouts | Unset |
| Fraud and privacy | ||
AE_FRAUD_DRYRUN | 1 scores sales without holding them | Off |
AE_IP_MODE | trunc24 trims each IP to its /24 (IPv4) or /48 (IPv6) before hashing, so the per-IP click speed rule counts by network | off |
AE_CLICK_RETENTION_DAYS | Days before it deletes raw clicks. 0 keeps them | 90 |
| Other | ||
AE_DEMO | 1 runs a public read-only demo that resets daily | Off |
MCP and postbacks
Both come with self-host and with cloud.
MCP server
Ask Claude, Cursor or any MCP client about your program. It reads, and never changes anything. Point your client at /mcp with an admin key as the bearer token.
Tools: earnings_summary, dashboard, fraud_queue, program_digest, usage_meter.
Postbacks
Send each sale to an affiliate's own tracker. Set a postback URL on the affiliate with PATCH /admin/affiliates/{id} (the same call pauses or resumes a partner with {"status":"frozen"} or "active"). Failed calls retry up to 5 times.
https://tracker.example/pb?order={order_id}
&amount={commission_cents}&cur={currency}
# Every call carries a signature
X-AE-Signature: t=<unix>,v1=<HMAC-SHA256>
Stuck on a step?
Ask the people who built it.
Send your version, what you ran and what came back. The output of doctor helps too.