The Developer’s Guide to Server-Side Affiliate Tracking

The Developer’s Guide to Server-Side Affiliate Tracking

In this article

Why Pixels Break (And Developers Get the Blame)

How Server-Side Affiliate Tracking Works

The Tapfiliate REST API: Your Implementation Toolkit

Implementation Walkthrough: Full S2S Setup

Edge Cases Developers Actually Hit

Privacy, Security, and GDPR

Testing Your S2S Implementation

FAQ

Close the Gap

TL;DR: Server-side affiliate tracking replaces browser-dependent pixels with direct API calls from your backend, capturing every conversion regardless of ad blockers, iOS privacy settings, or cookie restrictions.

  • S2S tracking stores a click ID server-side and fires a postback to Tapfiliate when a conversion occurs
  • The Tapfiliate REST API handles this in two core calls: POST /clicks/ on landing, POST /conversions/ on purchase
  • The hardest part is not the API call; it is correctly storing the click ID on the user’s first page load
  • Once live, attributed conversions rise from 60–70% (pixel, mobile) to near-complete accuracy

Your affiliate dashboard shows 847 conversions this month. Your order management system shows 1,203.

The gap is 356 sales your affiliate program cannot attribute. That’s 356 commissions calculated on guesswork, or 356 affiliate-driven sales that went unrewarded because a pixel failed to fire.

Either way, someone is losing money. When the marketing team asks why the numbers don’t match, the answer is always the same: “the pixel.”

This guide explains why pixels fail, how server-side tracking fixes the problem at the architecture level, and how to implement it against the Tapfiliate REST API with working code.

By the end, the gap between your order count and your attributed conversions will close permanently.

Why Pixels Break (And Developers Get the Blame)

The three failure modes of client-side tracking

A tracking pixel fires from the buyer’s browser when they reach your confirmation page. In practice, it fails in three distinct and predictable ways.

Ad blockers. Extensions like uBlock Origin and Privacy Badger block tracking scripts by default. Ad blocker adoption exceeded 40% on desktop in 2025 and continues to rise.

iOS and Safari ITP. Apple’s Intelligent Tracking Prevention caps third-party cookie lifespans at 24 hours after the user’s last interaction with the tracker. A buyer who clicks an affiliate link on Monday and converts on Wednesday has no trackable cookie. The pixel fires but has nothing to read.

Page abandonment before load. On slow mobile connections, JavaScript-heavy confirmation pages can take three to five seconds to fully render. Fast buyers close the tab. The pixel script never executes.

What the attribution gap costs in real numbers

The gap between pixel-tracked conversions and actual orders can reach 30–40% on mobile-heavy traffic when ITP cookie expiry, ad blockers, and page abandonment stack together.

On a program paying $20 average commission across 1,000 monthly conversions, that gap means $6,000–$8,000 in commissions that cannot be correctly assigned. Affiliates get credited for sales they didn’t drive, or not credited for sales they did. Neither outcome is sustainable.

Server-side tracking removes the browser from the equation entirely. The conversion event fires from your server to Tapfiliate’s API. No pixel. No browser dependency. No gap.

How Server-Side Affiliate Tracking Works

The click ID lifecycle

Everything in server-side affiliate tracking flows from one concept: the click ID. Understanding its lifecycle makes the implementation obvious.

Here is the complete end-to-end flow:

  1. A user clicks an affiliate link. The URL contains the affiliate’s referral code, for example, ?ref=sarah123.
  2. Your server receives the landing page request and detects the ref parameter in the URL.
  3. Your backend calls POST /clicks/ on the Tapfiliate REST API, passing the referral code.
  4. Tapfiliate creates a click record and returns a unique click ID for this specific event.
  5. Your backend stores the click ID, tied to the user’s server-side session or a database row.
  6. The user completes a purchase. Your order confirmation handler calls POST /conversions/, passing the stored click ID.
  7. Tapfiliate matches the click ID to the originating affiliate, calculates the commission, and credits the affiliate’s account.
  8. The affiliate sees the commission appear in their Tapfiliate dashboard in real time.

Steps 1–5 happen on the landing page request. Steps 6–8 happen on the order confirmation event. The buyer’s browser is involved only in Step 1, to deliver the ref parameter in the URL.

Client-side vs. server-side: the structural difference

In client-side tracking, the buyer’s browser is the source of truth. It holds the cookie, fires the pixel, and reports the conversion. Any failure on the buyer’s device means a lost attribution.

In server-side tracking, your backend is the source of truth. It captures the referral code on landing, creates the click, stores the click ID, and fires the conversion event at purchase. The buyer’s browser settings are irrelevant regardless of cookies, JavaScript blocking, or web vs. app context.

Client-Side (Pixel)Server-Side (S2S API)
Conversion signal sourceBuyer’s browserYour backend server
Ad blocker impactBlocks tracking scriptNo impact
iOS ITP impactExpires cookie within 24hNo impact
Attribution accuracy (mobile)60–70%Near-complete
Cross-device trackingNoYes (via session/account)
Setup complexityLowMedium
GDPR exposureHigher (browser-stored data)Lower (server-controlled)

The Tapfiliate REST API: Your Implementation Toolkit

Authentication

Every request to the Tapfiliate REST API requires one header:

X-Api-Key: YOUR_API_KEY

Your API key lives in your Tapfiliate account under Settings. Store it as an environment variable. Never hardcode it in source code. Never commit it to a version control repository. Never expose it in client-side JavaScript.

The API key can create conversions and approve commissions. An exposed key is a financial control risk, not just a tracking risk.

# Correct: environment variableexport TAPFILIATE_API_KEY="your_key_here"# Wrong: hardcodedconst apiKey = "tap_live_abc123xyz"; // Do not do this

All endpoints use Content-Type: application/json. All responses are JSON. The API is currently at version 1.6. For the full endpoint reference, see the Tapfiliate REST API documentation.

A quick API key security checklist before you write a single line of tracking code:

  • Store the key in an environment variable or a secrets manager (AWS Secrets Manager, Vault, GCP Secret Manager).
  • Rotate the key if it has ever been committed to a repository, even briefly, even in a private repo.
  • Set up an alert if the key is detected in any new commit using a tool like GitGuardian or GitHub secret scanning.
  • Use separate keys for staging and production. A staging test that fires on a production key creates real commissions.

None of this is unique to Tapfiliate. It applies to every API key your backend handles. But affiliate platform keys are particularly sensitive because they sit at the intersection of money and partner trust.

The three endpoints that matter

For server-side affiliate tracking, you need two core endpoints and one optional endpoint:

POST /clicks/
Creates a click record when a user arrives via an affiliate link. Pass the affiliate’s referral code. Tapfiliate returns a click object containing the click ID.

POST /conversions/
Registers a conversion against a stored click ID. Tapfiliate attributes the conversion to the correct affiliate and queues the commission for payout. Pass your internal order ID as external_id for deduplication.

POST /customers/ (optional)
Creates a persistent customer record. Relevant for subscription and SaaS programs where you track customer lifetime value and want recurring commission attribution across multiple billing events, not just first purchase.

Tapfiliate also supports fallback attribution via referral code or coupon code directly on POST /conversions/. If a user arrives without a ref parameter but uses an affiliate coupon at checkout, you can pass the coupon code on the conversion call and attribution still resolves correctly.

Implementation Walkthrough: Full S2S Setup

This walkthrough uses Node.js. The same pattern applies in Python, Go, Ruby, or any other backend language. Replace the fetch calls with your HTTP client of choice.

Step 1: Capture the affiliate ref parameter on landing

Every visitor to your site may or may not carry an affiliate referral code in the URL. Your landing middleware must check for it on every request and create a click when one is present.

// Express.js middleware, runs on every incoming page requestapp.use(async (req, res, next) => {  const affiliateRef = req.query.ref || req.query.tap_a;  if (affiliateRef) {    try {      const click = await createTapfiliateClick(affiliateRef);      // Store the click ID in the server-side session      req.session.tapClickId = click.id;    } catch (err) {      // Non-blocking: log the failure, do not prevent page load      console.error('Tapfiliate click creation failed:', err.message);    }  }  next();});

The try/catch here is intentional and important. Click creation should never block page rendering. If the Tapfiliate API is slow or temporarily unavailable, the user still reaches your site. You log the failure and move on.

Step 2: Store the click ID server-side

The createTapfiliateClick function calls POST /clicks/ and returns the click ID.

async function createTapfiliateClick(referralCode) {  const response = await fetch('https://api.tapfiliate.com/1.6/clicks/', {    method: 'POST',    headers: {      'Content-Type': 'application/json',      'X-Api-Key': process.env.TAPFILIATE_API_KEY,    },    body: JSON.stringify({      referral_code: referralCode,    }),  });  if (!response.ok) {    throw new Error(`Tapfiliate API returned ${response.status}`);  }  return response.json(); // Returns { id: "click_abc123...", ... }}

The id field in the response is the click ID. Store it in your server-side session store, a signed cookie with HttpOnly and Secure flags set, or a database row keyed to the user’s session identifier.

Do not store it in a client-accessible cookie or localStorage. That storage is visible to browser extensions and can be manipulated.

Step 3: Fire the conversion postback

When a purchase completes, call POST /conversions/ from your backend order handler. Never call this from a frontend script or tag manager.

async function recordTapfiliateConversion(clickId, orderId, orderValue) {  const response = await fetch('https://api.tapfiliate.com/1.6/conversions/', {    method: 'POST',    headers: {      'Content-Type': 'application/json',      'X-Api-Key': process.env.TAPFILIATE_API_KEY,    },    body: JSON.stringify({      click_id:    clickId,      external_id: orderId,    // Your internal order ID (used for deduplication)      amount:      orderValue, // Order value in your program's base currency    }),  });  if (!response.ok) {    const errorBody = await response.text();    console.error(`Tapfiliate conversion failed [${response.status}]: ${errorBody}`);    // Add to retry queue; do not silently discard  }}

Wire this into your post-payment webhook or order confirmation handler:

// Post-payment webhook endpointapp.post('/webhooks/payment-confirmed', async (req, res) => {  const { orderId, orderValue, sessionId } = req.body;  // Look up the click ID stored during the user's landing visit  const clickId = await getClickIdForSession(sessionId);  if (clickId) {    await recordTapfiliateConversion(clickId, orderId, orderValue);  }  res.sendStatus(200);});

In my experience, the single most common implementation error is firing this conversion call from the frontend “thank you” page using a script tag or GTM trigger. If the buyer closes the tab before the page fully loads (it happens more than you think), the conversion is silently lost. Always fire from the backend payment confirmation event.

Step 4: Verify in the Tapfiliate dashboard

After your first test transaction, open your Tapfiliate account and go to Conversions. Confirm the conversion appears with the correct affiliate, order amount, and a “Pending” status.

A 400 response from POST /conversions/ typically means the click ID is invalid or expired. A 401 means the API key header is missing or incorrect. For full error codes and additional parameters, including how to attach metadata or customer IDs to conversions, see Tapfiliate’s developer integration guides.

Edge Cases Developers Actually Hit

Missing click IDs (direct traffic and dark social)

Not every visitor arrives via an affiliate link. Direct traffic, email newsletters, SMS campaigns, and dark social shares carry no ref parameter. Your middleware finds nothing, no click ID is stored, and no affiliate is on record.

What I’ve noticed is that teams often forget to handle this path explicitly. Null click IDs passed to POST /conversions/ result in 400 errors that look like integration failures. They are not. They are expected cases that need a conditional branch.

// In your payment confirmation handlerif (clickId) {  await recordTapfiliateConversion(clickId, orderId, orderValue);} else {  // No affiliate referral detected; organic or direct conversion  logger.info('Conversion recorded without affiliate attribution', { orderId });}

For buyers who arrived without a ref parameter but used an affiliate coupon code at checkout, you can pass the coupon code directly on the conversion call instead of a click ID. Tapfiliate resolves attribution from the coupon.

Duplicate conversion prevention

Network timeouts cause webhooks to retry. Without deduplication, a single purchase fires two or three conversion events. Your affiliate program credits one sale multiple times.

The fix is the external_id field on POST /conversions/. Tapfiliate rejects any conversion submitted with an external_id that already exists in your account.

body: JSON.stringify({  click_id:    clickId,  external_id: orderId,   // Your stable internal order ID (safe to retry)  amount:      orderValue,}),

Always use your internal order ID as external_id. Always. Never generate a fresh ID per retry attempt. A stable order ID means the endpoint is idempotent. You can retry as many times as you need without creating duplicate commissions.

Multi-touch and cross-session attribution

A user often interacts with multiple affiliate links before converting. They click an Instagram post from affiliate A on Monday. They click a blog link from affiliate B on Thursday. They purchase on Friday.

Tapfiliate’s default model attributes the commission to the last click, which is affiliate B. If your program model uses first-touch attribution, you can preserve the first-stored click ID by checking whether one already exists in the session before overwriting it:

// Only store a click ID if one is not already present for this sessionif (!req.session.tapClickId) {  const click = await createTapfiliateClick(affiliateRef);  req.session.tapClickId = click.id;}

Which model you use depends on your program rules. The implementation is the same either way. The difference is whether you overwrite the stored click ID on subsequent affiliate link visits.

Privacy, Security, and GDPR

Never expose your API key client-side

The X-Api-Key header authenticates requests that create conversions and approve commissions. This is not a read-only analytics token.

If the key ends up in a browser network request (a React component, a GTM custom HTML tag, or a Segment source function) any user can extract it from browser devtools. They can then create fraudulent conversions against any affiliate in your program.

All Tapfiliate REST API calls must originate from your backend. Your frontend JavaScript has no legitimate reason to call the Tapfiliate API. If you run a client-side tag manager alongside your S2S implementation, ensure the tag manager integration uses a separate, scoped data layer. Never pass your API key through it.

GDPR and data minimization for click IDs

A click ID is a pseudonymous identifier that, when combined with other data, can be linked to an individual. Under GDPR, it may qualify as personal data depending on your processing context.

Practical compliance steps:

  1. Set a retention limit. Tapfiliate’s default attribution window is 30 days. Delete stored click IDs from your session store or database after 30 days if no conversion has occurred.
  2. Avoid unnecessary linking. Do not store click IDs in the same database row as directly identifying data (name, email) without a documented lawful basis.
  3. Include click ID handling in your Data Processing Agreement with Tapfiliate. The DPA covers the data Tapfiliate processes on your behalf.
  4. Honor deletion requests. Right-to-erasure requests must include deletion of stored click IDs linked to that user.

For programs with EU users, document click ID processing in your Records of Processing Activities. For a broader read on how S2S fits alongside influencer program tracking, see hybrid influencer tracking with affiliate software.

Testing Your S2S Implementation

Parallel tracking validation

Before switching off your existing pixel-based tracking, run both systems in parallel for a minimum of 48 hours. This lets you compare conversion counts and catch click ID storage issues without any attribution data loss.

The test protocol:

  1. Enable your S2S postback code alongside the existing client-side pixel.
  2. Generate 10–20 controlled test transactions using your own affiliate links.
  3. Compare the Tapfiliate conversion count against your order management system count.
  4. If counts match across all 10–20 test orders: S2S is working correctly. Disable the pixel.
  5. If there is a gap: check your click ID retrieval logic in the payment handler first. The most common cause is session expiry between landing and purchase for long-consideration purchases.

A practical way to test without real orders: use Tapfiliate’s test affiliate account and a staging environment. Create a test affiliate, click your own affiliate link in incognito mode, complete a test purchase, then check the conversion appears in the Tapfiliate dashboard. This gives you a clean end-to-end test without touching production data.

One more thing to verify: check that your external_id deduplication works. Submit the same test order ID twice and confirm the second call returns a 409 or similar duplicate rejection. If both calls succeed, your deduplication is not working and you will accumulate double-commissions when webhooks retry.

Tapfiliate’s conversion list shows each record’s attribution source. You can filter by date range and confirm conversions came through the S2S API path. If conversions appear under the JavaScript path during parallel testing, your S2S middleware is not capturing the click ID correctly on landing.

For larger programs migrating from a different affiliate platform entirely, see Tapfiliate’s affiliate program migration guide.

FAQ

What is server-side affiliate tracking?

Server-side affiliate tracking sends conversion events directly from your backend server to the affiliate platform’s API, instead of relying on a pixel firing in the buyer’s browser. Because the conversion signal comes from your server, it is not affected by ad blockers, iOS cookie restrictions, or page abandonment before JavaScript loads.

What is a postback URL in affiliate marketing?

A postback URL is the API endpoint your server calls to report a completed conversion. When a buyer purchases, your backend sends the stored click ID to the affiliate platform’s endpoint. The platform matches the ID to the original click, attributes the conversion to the correct affiliate, and calculates the commission owed.

What is a click ID in affiliate tracking?

A click ID is a unique identifier generated when a user clicks an affiliate link. Your backend captures the affiliate referral code from the URL, calls POST /clicks/, and receives the click ID in return. You store it server-side. At purchase, you send it with the conversion event, acting as the receipt that connects a specific click to a specific sale without any cookie.

How do I implement S2S tracking without cookies?

Capture the ref parameter from the URL on the user’s first landing page request. Call POST /clicks/ with the referral code and store the returned click ID in your server-side session store or database. When the user converts, call POST /conversions/ with the stored click ID from your backend payment handler. No cookie is involved at any step. See the Tapfiliate REST API documentation for full parameter reference and response schemas.

Is server-side affiliate tracking GDPR compliant?

Yes, when implemented correctly. S2S tracking does not use third-party cookies and gives you direct control over what identifiers are stored, for how long, and under what lawful basis. You must document click ID processing in your Records of Processing Activities, honor erasure requests, and apply data minimization. Compared to pixel-based tracking, server-side tracking typically reduces your GDPR exposure because the browser is no longer the data collection point.

Close the Gap

Your order management system already has the truth. Every confirmed purchase is in there.

The only question is whether your affiliate program knows it too.

Three API calls separate a 65% attribution rate from near-complete attribution. POST /clicks/ on landing. Click ID stored in session. POST /conversions/ on purchase confirmation.

The affiliate who drove 847 of those 1,203 sales deserves accurate attribution for all 847. Your program deserves the data to know which traffic sources actually convert, and which do not.

Start your free Tapfiliate trial and wire your backend directly to the Tapfiliate REST API. No credit card required. Trusted by 69,500+ marketers and engineering teams.

Jessica Rangel

Jessica Rangel

Spending my days writing marketing content, cycling around canals in Amsterdam, and attempting to master the Dutch language.

In this article

Why Pixels Break (And Developers Get the Blame)

How Server-Side Affiliate Tracking Works

The Tapfiliate REST API: Your Implementation Toolkit

Implementation Walkthrough: Full S2S Setup

Edge Cases Developers Actually Hit

Privacy, Security, and GDPR

Testing Your S2S Implementation

FAQ

Close the Gap

Mobile sign-up can be tricky

Drop your contact info, and get a detailed guide to test Tapfiliate faster and effectively

I consent to processing of my personal data, and confirm that I have read and understood the Privacy Policy of Tapfiliate.
Sign up on mobile
Tapfiliate blog subscribe

Don’t miss what matters in affiliate marketing.

We pick the best and send it to your inbox.