Affiliate URLs enable commission tracking and partner attribution. A well-designed affiliate system needs reliable tracking, fraud prevention, and clear attribution rules.
Key Takeaways
- 1Use unique affiliate IDs that are hard to guess
- 2Store affiliate attribution in cookies for persistence
- 3Define clear attribution windows (30-90 days typical)
- 4Track both clicks and conversions for fraud detection
- 5Provide affiliates with link builders and tracking tools
“Affiliate links must be clearly disclosed to consumers. The material connection between the endorser and the advertiser must be clearly and conspicuously disclosed.”
Affiliate Parameters
Affiliate URLs typically include several parameters that identify the partner and enable tracking. The table below shows common parameters you will encounter in affiliate programs.
| Parameter | Purpose | Example |
|---|---|---|
| ref / aff | Affiliate ID | ref=ABC123 |
| subid | Sub-affiliate tracking | subid=landing-page-1 |
| campaign | Campaign identifier | campaign=summer-sale |
| source | Traffic source | source=youtube |
| click_id | Unique click ID | click_id=xyz789 |
Sub-IDs let affiliates track which of their own campaigns drive conversions. Click IDs enable individual-level tracking for fraud detection and accurate attribution.
URL Structure
Affiliate URLs can take several forms depending on your technical requirements and how much branding flexibility you want to give partners. Here are common patterns.
# Affiliate URL patterns
# Direct product link with affiliate ID
https://shop.com/product/shoes?ref=ABC123
# Dedicated affiliate subdomain
https://go.shop.com/ABC123/product/shoes
# Redirect through affiliate URL
https://shop.com/go/ABC123?url=/product/shoes
# Short link format
https://shop.com/r/ABC123
# With sub-tracking
https://shop.com/product/shoes?ref=ABC123&subid=youtube-review
# Full tracking URL
https://shop.com/product/shoes?ref=ABC123&subid=email-campaign&click_id=xyz789Query parameter links are simplest to implement. Redirect URLs give you a central point for click tracking. Short links are easiest for affiliates to share on social media.
On the backend, you need to capture affiliate information from incoming requests and store it for later conversion attribution. Here is how to implement this middleware.
Implementation
When a user arrives via an affiliate link, you need to validate the affiliate, record the click, and store attribution data in a cookie for later conversion tracking.
// Affiliate tracking middleware
function affiliateMiddleware(req, res, next) {
const refId = req.query.ref || req.query.aff;
const subId = req.query.subid;
const clickId = req.query.click_id || generateClickId();
if (refId) {
// Validate affiliate exists
const affiliate = await db.affiliates.findOne({ id: refId });
if (!affiliate || !affiliate.active) {
return next(); // Ignore invalid affiliate
}
// Set attribution cookie
const cookieData = {
affiliateId: refId,
subId: subId,
clickId: clickId,
timestamp: Date.now(),
landingPage: req.path
};
res.cookie('affiliate', JSON.stringify(cookieData), {
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
httpOnly: true,
secure: true,
sameSite: 'lax'
});
// Log click for analytics
await db.affiliateClicks.create({
affiliateId: refId,
subId: subId,
clickId: clickId,
ip: req.ip,
userAgent: req.headers['user-agent'],
landingPage: req.path,
referrer: req.headers['referer']
});
}
next();
}
function generateClickId() {
return crypto.randomBytes(16).toString('hex');
}This middleware validates the affiliate ID, sets a 30-day attribution cookie, and logs the click with contextual information. Invalid or inactive affiliates are silently ignored to prevent error pages.
Recording clicks is only half the story. When a purchase happens, you need to attribute it back to the original affiliate and calculate their commission.
Conversion Attribution
When a user completes a purchase, check for the affiliate attribution cookie, verify the attribution window has not expired, and create a conversion record with the calculated commission.
// Attribute conversion to affiliate
async function trackConversion(order, req) {
const affiliateCookie = req.cookies.affiliate;
if (!affiliateCookie) {
return null;
}
const attribution = JSON.parse(affiliateCookie);
const affiliate = await db.affiliates.findOne({ id: attribution.affiliateId });
if (!affiliate) {
return null;
}
// Check attribution window
const windowMs = affiliate.attributionWindow * 24 * 60 * 60 * 1000;
if (Date.now() - attribution.timestamp > windowMs) {
return null; // Attribution expired
}
// Calculate commission
const commission = calculateCommission(order, affiliate);
// Create conversion record
const conversion = await db.conversions.create({
orderId: order.id,
affiliateId: affiliate.id,
subId: attribution.subId,
clickId: attribution.clickId,
orderTotal: order.total,
commission: commission,
status: 'pending',
attributedAt: new Date()
});
// Notify affiliate (optional)
await notifyAffiliateOfConversion(affiliate, conversion);
return conversion;
}
function calculateCommission(order, affiliate) {
if (affiliate.commissionType === 'percentage') {
return order.total * (affiliate.commissionRate / 100);
} else {
return affiliate.commissionRate; // Flat rate
}
}The attribution check ensures conversions outside the defined window (typically 30 days) are not credited. Commission calculation supports both percentage and flat-rate models per affiliate.
Affiliates need an easy way to generate tracked links for any page on your site. A link builder tool improves their experience and ensures consistent tracking.
Affiliate Link Builder
Providing affiliates with a dashboard that generates properly formatted links reduces errors and helps them track performance across their campaigns.
// Build affiliate URLs for partners
function buildAffiliateUrl(baseUrl, affiliateId, options = {}) {
const url = new URL(baseUrl);
// Add affiliate ID
url.searchParams.set('ref', affiliateId);
// Add sub-tracking
if (options.subId) {
url.searchParams.set('subid', options.subId);
}
// Add campaign tracking
if (options.campaign) {
url.searchParams.set('campaign', options.campaign);
}
// Generate unique click ID for tracking
url.searchParams.set('click_id', generateClickId());
return url.toString();
}
// Affiliate dashboard link generator
function getAffiliateDashboardLinks(affiliateId) {
const baseUrls = [
'https://shop.com',
'https://shop.com/products',
'https://shop.com/deals'
];
return baseUrls.map(base => ({
name: new URL(base).pathname || 'Home',
url: buildAffiliateUrl(base, affiliateId),
shortUrl: `https://shop.com/r/${affiliateId}`
}));
}
// Deep link generator for specific products
function getProductAffiliateLink(productUrl, affiliateId, source) {
return buildAffiliateUrl(productUrl, affiliateId, {
subId: source,
campaign: 'product-link'
});
}These functions help affiliates generate links with proper tracking parameters. Each link includes a unique click ID so you can trace conversions back to specific clicks.
Affiliate programs are targets for fraud. Click farms, self-referrals, and fake conversions can cost you money if you do not have detection systems in place.
Fraud Prevention
Detecting affiliate fraud requires analyzing patterns across clicks and conversions. Unusual velocity, repeated IPs, or suspiciously high conversion rates are red flags that warrant investigation.
// Detect suspicious affiliate activity
async function detectFraud(click) {
const flags = [];
// Check click velocity
const recentClicks = await db.affiliateClicks.count({
affiliateId: click.affiliateId,
timestamp: { $gt: Date.now() - 3600000 } // Last hour
});
if (recentClicks > 1000) {
flags.push('high_click_volume');
}
// Check IP patterns
const ipClicks = await db.affiliateClicks.count({
ip: click.ip,
timestamp: { $gt: Date.now() - 86400000 } // Last 24h
});
if (ipClicks > 10) {
flags.push('ip_reuse');
}
// Check conversion rate (too high is suspicious)
const stats = await getAffiliateStats(click.affiliateId);
if (stats.conversionRate > 0.5) { // 50% conversion rate
flags.push('suspicious_conversion_rate');
}
// Check for self-referral
if (await isSelfReferral(click)) {
flags.push('self_referral');
}
return flags;
}
// Hold commissions for review
async function processConversion(conversion) {
const flags = await detectFraud(conversion);
if (flags.length > 0) {
conversion.status = 'under_review';
conversion.fraudFlags = flags;
} else {
conversion.status = 'approved';
}
await conversion.save();
}Flagged conversions are held for manual review rather than automatically rejected. This gives you time to investigate while protecting against automated fraud.