3-Step Integration Guide
Get Affitor tracking live with the supported click, signup, and server-side tracking flows
Three steps get Affitor tracking live: clicks → signups → sales. Steps 1 and 2 establish attribution; step 3 records revenue.
Before You Start
Make sure you have:
- Your program ID
- Your program API key for server-to-server calls
- A decision on how you will track sales (server-side tracking vs Stripe integration):
- Server-side tracking — your backend calls
POST /api/v1/track/sale— for any backend or payment provider - Stripe integration — attach Affitor metadata to Stripe Checkout sessions
- Server-side tracking — your backend calls
Use one naming scheme consistently:
customerKey— browser helper argument (signup(customerKey, email))customer_key— tracking API fieldaffitor_customer_key— Stripe metadata field
Step 1 — Install the Tracker
Add the tracker to every page where affiliate traffic can land.
Once the tracker is live on your public site, Affitor detects it automatically — no debug mode needed. To verify the full pipeline, use the Run verification button on the Share Program step (it runs a synthetic click → signup → sale). See Testing Integration.
<script
src="https://api.affitor.com/js/affitor-tracker.js"
data-affitor-program-id="YOUR_PROGRAM_ID"
data-affitor-debug="false">
</script>Replace YOUR_PROGRAM_ID with the program ID from your dashboard.
What this does
- Detects affiliate visits via the
?aff=parameter - Creates a tracked customer / click relationship
- Stores
affitor_click_idin a first-party cookie - Sends click/pageview tracking data to Affitor
Verify
Visit your site through a real affiliate link or enable debug mode while testing. Then review:
- browser console / network requests
- Affitor dashboard → tracking/integration pages
Step 2 — Track Signups
After signup succeeds, tell Affitor which internal user was created.
Option A — Browser-side helper
Use this when your signup flow runs in the browser and the tracker is already loaded.
<script>
await window.affitor.signup('user_123', 'user@example.com');
</script>Or inside your signup flow:
<script>
document.getElementById('signup-form').addEventListener('submit', async function(e) {
e.preventDefault();
const email = document.getElementById('email').value;
// Run your own signup logic first.
// After account creation succeeds:
await window.affitor.signup(currentUser.id, email);
});
</script>Use this when:
- you already installed the tracker on the page
- signup happens in the browser
- you want the simplest supported flow
Option B — Server-side lead API
Use this when signup logic lives on your backend or you want the server to own the tracking call.
POST https://api.affitor.com/api/v1/track/lead
Authorization: Bearer YOUR_PROGRAM_API_KEY
Content-Type: application/json
{
"click_id": "cust_42_1234567890",
"customer_key": "user_123",
"email": "user@example.com"
}await fetch('https://api.affitor.com/api/v1/track/lead', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.AFFITOR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
click_id: req.cookies.affitor_click_id,
customer_key: newUser.id,
email: newUser.email,
}),
});Lead tracking rules
- Outside test mode, provide at least one of:
click_idcustomer_key
- For reliable payment attribution later, always send a stable internal customer ID:
customerKeyinsignup()customer_keyin the API
- Browser-side
signup()is the easiest path for most teams - Server-side API is best when signup completes on your backend
Response:
{
"success": true,
"message": "Lead tracked successfully"
}Step 3 — Track Sales
When a customer pays, choose one revenue-tracking path.
Option A — Server-side tracking (works with any payment provider)
Send a server-to-server request once payment succeeds.
Endpoint: POST https://api.affitor.com/api/v1/track/sale
{
"transaction_id": "txn_abc123",
"customer_key": "user_123",
"click_id": "cust_42_123456",
"amount_cents": 9999,
"currency": "USD",
"sale_type": "payment",
"is_recurring": false,
"subscription_id": "sub_xyz",
"subscription_interval": "monthly",
"product_id": "prod_456",
"line_items": [
{ "name": "Pro Plan", "amount_cents": 9999, "quantity": 1 }
]
}| Parameter | Required | Type | Description |
|---|---|---|---|
transaction_id | Yes | string | Unique transaction identifier used for deduplication |
amount_cents | Yes | number | Positive integer amount in cents |
customer_key | Conditional | string | Your internal customer ID |
click_id | Conditional | string | Tracked click identifier |
currency | No | string | USD, EUR, or VND. Default: USD |
sale_type | No | string | payment or subscription. Default: payment |
is_recurring | No | boolean | Use true for recurring charges |
subscription_id | No | string | External subscription ID |
subscription_interval | No | string | e.g. monthly, quarterly, annual |
product_id | No | string | External product identifier |
line_items | No | array | Optional item breakdown |
await fetch('https://api.affitor.com/api/v1/track/sale', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.AFFITOR_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
transaction_id: order.id,
customer_key: currentUser.id,
click_id: req.cookies.affitor_click_id,
amount_cents: order.totalCents,
currency: 'USD',
sale_type: 'payment',
}),
});Response:
{
"success": true,
"sale_id": 42,
"commission_id": 18,
"message": "Sale tracked successfully"
}transaction_id must be unique. Reusing the same value returns 409 Conflict.
Option B — Stripe integration
If you already use Stripe Checkout, keep taking payment in your own Stripe account — just attach Affitor metadata to the checkout session.
const session = await stripe.checkout.sessions.create({
line_items: [{ price: 'price_xxx', quantity: 1 }],
mode: 'payment',
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/cancel',
metadata: {
affitor_click_id: clickId,
affitor_customer_key: currentUser.id,
program_id: 'YOUR_PROGRAM_ID',
},
});For subscriptions
Include the same fields in both places:
metadatasubscription_data.metadata
const session = await stripe.checkout.sessions.create({
line_items: [{ price: 'price_xxx', quantity: 1 }],
mode: 'subscription',
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/cancel',
metadata: {
affitor_click_id: clickId,
affitor_customer_key: currentUser.id,
program_id: 'YOUR_PROGRAM_ID',
},
subscription_data: {
metadata: {
affitor_click_id: clickId,
affitor_customer_key: currentUser.id,
program_id: 'YOUR_PROGRAM_ID',
},
},
});Stripe integration rules
- One-time payments are attributed from Stripe checkout metadata and webhook processing
- Subscriptions and renewals rely on
invoice.payment_succeeded - If
subscription_data.metadatais missing, renewal attribution can fail - Required metadata fields:
affitor_click_idaffitor_customer_keyprogram_id
Which Sales Path Should You Use?
| Path | Best for |
|---|---|
Server-side tracking — POST /api/v1/track/sale | Any backend, any payment provider, custom checkout, Paddle/LemonSqueezy/manual server events |
| Stripe integration | Existing Stripe Checkout integration |
Use server-side tracking when your backend controls the revenue event. Use Stripe integration when you want Affitor to attribute sales directly from your Stripe webhook flow.
You're Done
All three steps working means your integration is live:
| Step | Event | Outcome |
|---|---|---|
| Tracker installed | Click/pageview tracking | affiliate visit captured |
| Signup tracked | Lead event | customer linked to partner |
| Sale tracked | Sale + commission flow | revenue attributed |
Next: