Lead Tracking (Signup)
Lead tracking captures when referred visitors sign up or submit forms. This lets you see which partners drive qualified leads—not just clicks.
What It Does
Section titled “What It Does”When a visitor signs up on your site, you call trackLead() to record the lead. This:
- Validates that
user_idis provided (critical for payment tracking) - Links the lead event to the stored
customer_codefrom the click - Sends lead data to Affitor API for processing
- Updates the customer-partner relationship status to “lead”
Partners can then see: Clicks → Signups → Sales
Note: In debug mode, calling trackLead() sends a test event instead of a real lead event. This helps verify your integration without creating actual lead records.
Prerequisites
Section titled “Prerequisites”Before implementing lead tracking:
- Pageview tracker installed (guide)
- Visitor arrived via affiliate link and has been tracked (creates
customer_codein database) -
customer_codecookie exists in browser (set automatically by pageview tracker)
Important: The lead event requires the customer_code cookie to link the signup to the affiliate partner. If this cookie is missing or the customer wasn’t tracked via an affiliate link, the lead tracking will fail silently (no error, but lead won’t be attributed).
Basic Implementation
Section titled “Basic Implementation”After your signup form submits successfully, call trackLead().
npm SDK (Recommended)
Section titled “npm SDK (Recommended)”import { loadAffitor } from '@affitor/tracker';
// Awaits script load — guaranteed to fireconst affitor = await loadAffitor('YOUR_PROGRAM_ID');affitor?.trackLead({ email: 'user@example.com', user_id: 'user_123' // Your internal user ID - REQUIRED});Script Tag
Section titled “Script Tag”var leadData = { email: 'user@example.com', user_id: 'user_123' // Your internal user ID - REQUIRED};
if (window.affitor) { window.affitor.trackLead(leadData);} else { window.affitorQueue = window.affitorQueue || []; window.affitorQueue.push(['trackLead', leadData]);}Full Implementation
Section titled “Full Implementation”Include additional data for better analytics:
npm SDK
Section titled “npm SDK”import { loadAffitor } from '@affitor/tracker';
const affitor = await loadAffitor('YOUR_PROGRAM_ID');affitor?.trackLead({ email: 'user@example.com', // Required user_id: 'user_123', // Required - YOUR internal user ID additional_data: { signup_method: 'email', // Optional - How they signed up plan: 'free_trial', // Optional - Plan they selected source: 'pricing_page' // Optional - Where they signed up }});Script Tag
Section titled “Script Tag”var leadData = { email: 'user@example.com', // Required user_id: 'user_123', // Required - YOUR internal user ID name: 'John Doe', // Optional phone: '+1234567890', // Optional additional_data: { signup_method: 'email', // Optional - How they signed up plan: 'free_trial', // Optional - Plan they selected source: 'pricing_page' // Optional - Where they signed up }};
if (window.affitor) { window.affitor.trackLead(leadData);} else { window.affitorQueue = window.affitorQueue || []; window.affitorQueue.push(['trackLead', leadData]);}Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Recommended | User’s email address (hashed for privacy before storage) |
user_id | string | Required | Your internal user ID - critical for payment attribution |
name | string | No | User’s full name (NOT stored - discarded per privacy policy) |
phone | string | No | User’s phone number (NOT stored - discarded per privacy policy) |
additional_data | object | No | Any extra data you want to track (stored as-is) |
Privacy Notes:
emailis hashed (SHA-256) and masked before storage - the original email is never storednameandphoneare intentionally discarded by the backend and never stored- Only
email_hash,email_masked, anduser_idare persisted in the database
Integration Examples
Section titled “Integration Examples”React / Next.js (npm SDK)
Section titled “React / Next.js (npm SDK)”import { loadAffitor } from '@affitor/tracker';
function SignupForm() { const handleSubmit = async (e) => { e.preventDefault(); const formData = new FormData(e.target);
// Your signup logic const result = await api.createAccount({ email: formData.get('email'), name: formData.get('name') });
if (result.success) { // ✅ Awaits script load — guaranteed to fire const affitor = await loadAffitor('YOUR_PROGRAM_ID'); affitor?.trackLead({ email: formData.get('email') as string, user_id: result.userId, // ✅ REQUIRED: Your internal user ID additional_data: { signup_method: 'react_form' } });
router.push('/welcome'); } };
return ( <form onSubmit={handleSubmit}> <input name="email" type="email" required /> <input name="name" type="text" /> <button type="submit">Sign Up</button> </form> );}Google/OAuth Signup (npm SDK)
Section titled “Google/OAuth Signup (npm SDK)”import { loadAffitor } from '@affitor/tracker';
async function handleOAuthCallback(user) { // User authenticated via Google/GitHub/etc.
// ✅ Awaits script load — guaranteed to fire const affitor = await loadAffitor('YOUR_PROGRAM_ID'); affitor?.trackLead({ email: user.email, user_id: user.id, // ✅ REQUIRED: Your internal user ID additional_data: { signup_method: 'google' } });
window.location.href = '/dashboard';}Standard Form (Script Tag)
Section titled “Standard Form (Script Tag)”document.getElementById('signup-form').addEventListener('submit', async function(e) { e.preventDefault();
var email = document.getElementById('email').value; var name = document.getElementById('name').value;
// Your signup logic var result = await createAccount(email, name);
if (result.success) { // Track the lead with user_id (REQUIRED) var leadData = { email: email, user_id: result.userId, // ✅ REQUIRED: Your internal user ID name: name, additional_data: { signup_method: 'form' } };
if (window.affitor) { window.affitor.trackLead(leadData); } else { window.affitorQueue = window.affitorQueue || []; window.affitorQueue.push(['trackLead', leadData]); }
window.location.href = '/welcome'; }});When to Call trackLead()
Section titled “When to Call trackLead()”Call trackLead() after the signup is confirmed successful:
| Scenario | When to track |
|---|---|
| Form signup | After form validation passes and account is created |
| OAuth signup | After OAuth callback, user authenticated |
| Email verification | After signup, not after verification (track intent) |
| Free trial | When trial starts |
| Waitlist | When added to waitlist |
Don’t track:
- Failed signups
- Duplicate signups
- Bot submissions
npm SDK vs Script Tag
Section titled “npm SDK vs Script Tag”npm SDK — await loadAffitor() (Recommended)
Section titled “npm SDK — await loadAffitor() (Recommended)”The npm SDK solves all timing issues with a Promise-based API. loadAffitor() returns a singleton Promise that resolves once the tracker script is loaded. No race conditions, no queue management, full TypeScript support.
import { loadAffitor } from '@affitor/tracker';
// Always works — awaits script load automaticallyconst affitor = await loadAffitor('YOUR_PROGRAM_ID');affitor?.trackLead({ email, user_id: userId });Script Tag — Check window.affitor First
Section titled “Script Tag — Check window.affitor First”If you’re using the script tag approach, always check if window.affitor exists first. If it does, call the method directly. If not, use the queue as a fallback for the case where the script hasn’t loaded yet.
var leadData = { email: email, user_id: userId };
if (window.affitor) { window.affitor.trackLead(leadData);} else { window.affitorQueue = window.affitorQueue || []; window.affitorQueue.push(['trackLead', leadData]);}Server-Side API
Section titled “Server-Side API”If you process signups on your backend server, you can track leads directly via the REST API instead of using client-side JavaScript. This is ideal for server-rendered apps or when you want to avoid any client-side tracking code on your signup page.
When to Use
Section titled “When to Use”Client-side (trackLead()) | Server-side API |
|---|---|
| Simple integration | No JS required on signup page |
| Requires affitor-tracker.js | Works in any server language |
| Cookie read by browser JS | Cookie read by your server |
| Best for SPAs / client-rendered apps | Best for server-rendered apps |
Both approaches use customer_code for attribution. The client-side trackLead() reads the cookie automatically; the server-side API requires your server to read the cookie value and pass it explicitly.
Authentication
Section titled “Authentication”Authorization: Bearer affitor_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxFind your API token in the dashboard under Settings → Referrals Tracker → Server-Side API.
How It Works
Section titled “How It Works”1. User visits your landing page with ?ref=partner1232. Affitor client-side tracker fires → sets affitor_customer cookie3. User fills out your signup form and submits4. Your frontend reads cookie for affitor_customer → sends value to your server5. Your server calls the Affitor API with customer_code6. Lead is attributed to the correct affiliate partnerAPI Request
Section titled “API Request”POST https://api.affitor.com/api/tracking/leadAuthorization: Bearer affitor_xxxxxxContent-Type: application/json
{ "customer_code": "cust_42_1234567890", // REQUIRED — from affitor_customer cookie "user_id": "usr_abc123", // REQUIRED "email": "user@example.com" // optional}Response
Section titled “Response”{ "success": true, "message": "Lead tracked successfully", "customer_code": "cust_42_1234567890"}Code Examples
Section titled “Code Examples”Node.js (Express)
Section titled “Node.js (Express)”app.post('/signup', async (req, res) => { const { email, password } = req.body;
// 1. Create user in your database const newUser = await createUser({ email, password });
// 2. Read the affitor_customer cookie (set by client-side tracker) const customerCode = req.cookies.affitor_customer;
// 3. Track lead via Affitor API (only if customer came via affiliate link) if (customerCode) { await fetch('https://api.affitor.com/api/tracking/lead', { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.AFFITOR_API_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ customer_code: customerCode, user_id: newUser.id, email: newUser.email, }), }); }
res.json({ success: true });});Python (Flask)
Section titled “Python (Flask)”@app.route('/signup', methods=['POST'])def signup(): data = request.get_json()
# 1. Create user in your database new_user = create_user(data['email'], data['password'])
# 2. Read the affitor_customer cookie customer_code = request.cookies.get('affitor_customer')
# 3. Track lead via Affitor API if customer_code: requests.post( 'https://api.affitor.com/api/tracking/lead', headers={ 'Authorization': f'Bearer {AFFITOR_API_TOKEN}', 'Content-Type': 'application/json', }, json={ 'customer_code': customer_code, 'user_id': str(new_user.id), 'email': new_user.email, }, )
return jsonify({'success': True})PHP (Laravel)
Section titled “PHP (Laravel)”public function signup(Request $request) { // 1. Create user in your database $user = User::create([ 'email' => $request->email, 'password' => bcrypt($request->password), ]);
// 2. Read the affitor_customer cookie $customerCode = $request->cookie('affitor_customer');
// 3. Track lead via Affitor API if ($customerCode) { Http::withHeaders([ 'Authorization' => 'Bearer ' . env('AFFITOR_API_TOKEN'), ])->post('https://api.affitor.com/api/tracking/lead', [ 'customer_code' => $customerCode, 'user_id' => (string) $user->id, 'email' => $user->email, ]); }
return response()->json(['success' => true]);}Ruby (Rails)
Section titled “Ruby (Rails)”def signup # 1. Create user in your database user = User.create!(email: params[:email], password: params[:password])
# 2. Read the affitor_customer cookie customer_code = cookies[:affitor_customer]
# 3. Track lead via Affitor API if customer_code.present? uri = URI('https://api.affitor.com/api/tracking/lead') http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true
req = Net::HTTP::Post.new(uri, { 'Authorization' => "Bearer #{ENV['AFFITOR_API_TOKEN']}", 'Content-Type' => 'application/json', }) req.body = { customer_code: customer_code, user_id: user.id.to_s, email: user.email, }.to_json
http.request(req) end
render json: { success: true }endTesting Your Integration
Section titled “Testing Your Integration”To verify your API token and integration work correctly, add "test_mode": true to the additional_data field:
{ "additional_data": { "test_mode": true }}In test mode:
customer_codeanduser_idare not required- No real lead record is created
- A test event is recorded that the dashboard can verify
After sending a test request, go to Settings → Referrals Tracker and click Test Lead Tracking to confirm the event was received. Remove test_mode when you go to production.
Error Responses
Section titled “Error Responses”| Status | Meaning |
|---|---|
401 | Missing or invalid API token |
403 | Program is not active |
400 | Missing customer_code or user_id |
404 | Customer not found or not in this program |
500 | Internal server error |
Verifying Lead Tracking
Section titled “Verifying Lead Tracking”Test Mode
Section titled “Test Mode”Enable debug mode in the pageview tracker:
npm SDK:
const affitor = await loadAffitor('YOUR_PROGRAM_ID', { debug: true });Script tag:
<script src="https://api.affitor.com/js/affitor-tracker.js" data-affitor-program-id="YOUR_PROGRAM_ID" data-affitor-debug="true"></script>What happens when you call trackLead() in debug mode:
- The tracker sends a test event instead of a real lead event
- No actual lead record is created in the database
- Console shows validation messages for
user_id - You’ll see these console messages:
With user_id provided:
[Affitor Tracker] ✅ Affitor: user_id detected - user_123[Affitor Tracker] trackTest() called true[Affitor Tracker] Sending test event to verify tracking: {customerCode: ..., hasAttribution: true, eventType: "test"}Without user_id:
[Affitor Tracker] ❌ Affitor: user_id is REQUIRED for trackLead() - payment attribution will not work without it![Affitor Tracker] Please include user_id in your trackLead call:[Affitor Tracker] window.affitor.trackLead({ email: "user@example.com", user_id: "user_123", ... })[Affitor Tracker] trackTest() called trueImportant: In debug mode, trackLead() always sends a test event and returns immediately without calling the real API. This prevents accidental test leads in your database.
Dashboard Verification
Section titled “Dashboard Verification”- Visit your site
- Go to Affitor dashboard → Setting -> Referrals Tracker
- Click on Test Lead Tracking
- Referrals Tracker should show “Connected” with a blue checkmark
Troubleshooting
Section titled “Troubleshooting”trackLead() Not Working
Section titled “trackLead() Not Working”Check:
window.affitorexists (pageview tracker loaded)customer_codecookie exists (visitor came via affiliate link)- No JavaScript errors in console
- Not in debug mode (debug mode sends test events only)
Lead Not Attributed to Partner
Section titled “Lead Not Attributed to Partner”Check:
- Visitor came via affiliate link (
?ref=PARTNER123) before signing up customer_codecookie exists and matches a tracked click in the database- Attribution window hasn’t expired (60 days default)
- Affiliate customer record exists in database (created during click tracking)
Common issue: If the visitor’s first page load didn’t include ?ref=, no customer record was created, so the lead cannot be attributed even if cookies exist.
user_id Warning Appears
Section titled “user_id Warning Appears”This is expected behavior when user_id is missing:
❌ Affitor: user_id is REQUIRED for trackLead() - payment attribution will not work without it!Solutions:
- Always include
user_idin yourtrackLead()call user_idshould be your internal user identifier (from your database)- The same
user_idmust be used in Stripe checkout metadata
Note: The lead is still tracked even without user_id, but payment attribution will fail later.
Duplicate Leads
Section titled “Duplicate Leads”Prevent by:
- Only calling
trackLead()once per signup - Checking signup success before tracking
- Backend automatically handles duplicate lead tracking (updates existing customer instead of creating new lead)
Lead Tracked But Not Showing in Dashboard
Section titled “Lead Tracked But Not Showing in Dashboard”Check:
- Lead event was successfully sent (check browser Network tab)
- Customer-partner relationship exists in database
- Lead status updated to “lead” (was previously “click”)
- Dashboard filters aren’t hiding the lead
Next Steps
Section titled “Next Steps”Lead tracking is set up. Now capture sales: