The Developer’s Guide to Server-Side Affiliate Tracking

The Developer’s Guide to Server-Side Affiliate Tracking

In this article

Why Pixels Fail (and Why Developers Get Blamed)

How Server-Side Affiliate Tracking Works

The Tapfiliate REST API: Your Implementation Toolkit

Implementation Guide: Complete S2S Setup

Edge Cases Developers Face

Privacy, Security, and GDPR

Testing Your S2S Implementation

FAQ

Close the Gap

Quick summary: 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 on the server and sends a postback to Tapfiliate when a conversion occurs.
  • The Tapfiliate REST API handles this through two main calls: POST /clicks/ on arrival and POST /conversions/ on purchase.
  • The most complex part is not the API call, but correctly storing the click ID on the user’s first page load.
  • Once live, attributed conversions increase from 60–70% accuracy with pixel-based mobile tracking to near-complete accuracy.

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

The gap represents 356 sales your affiliate program failed to attribute. That means either 356 commissions calculated using estimates or 356 affiliate-generated sales that went unrewarded because a pixel did not fire.

Either way, someone loses money. When the marketing team asks why the numbers do not match, the answer is always the same: “the pixel.”

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

By the end, the discrepancy between your order count and attributed conversions will be gone for good.

Why Pixels Fail (and Why Developers Get Blamed)

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 such as uBlock Origin and Privacy Badger block tracking scripts by default. Ad blocker adoption exceeded 40% on desktop in 2025 and continues to grow.

iOS and Safari ITP. Apple’s Intelligent Tracking Prevention limits the lifespan of third-party cookies to 24 hours after the user’s last interaction with the tracker. A buyer who clicks an affiliate link on Monday and converts on Wednesday will not have a trackable cookie. The pixel fires, but there is nothing to read.

Leaving the page before it loads. On slow mobile connections, JavaScript-heavy confirmation pages may take three to five seconds to load fully. Fast-moving buyers close the tab. The pixel script never runs.

The real cost of the attribution gap

The difference between pixel-tracked conversions and actual orders can reach 30–40% for predominantly mobile traffic when you combine ITP cookie expiration, ad blockers, and page abandonment.

For a program paying an average commission of $20 on 1,000 monthly conversions, that gap represents $6,000–$8,000 in misattributed commissions. Affiliates either receive credit for sales they did not generate or miss credit for sales they did generate. Neither scenario is sustainable.

Server-side tracking removes the browser dependency entirely. The conversion event is sent from your server to the Tapfiliate API. No pixel. No browser dependency. No gaps.

How Server-Side Affiliate Tracking Works

The click ID lifecycle

Everything in server-side affiliate tracking comes down to one concept: the click ID. Once you understand its lifecycle, the implementation becomes straightforward.

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 makes a POST /clicks/ call to the Tapfiliate REST API, passing the referral code.
  4. Tapfiliate creates a click record and returns a unique click ID for that specific event.
  5. Your backend stores the click ID, linked to the user’s server-side session or a database row.
  6. The user completes a purchase. Your order confirmation handler makes a POST /conversions/ call, passing the stored click ID.
  7. Tapfiliate matches the click ID to the referring affiliate, calculates the commission, and credits the affiliate’s account.
  8. The affiliate sees the commission appear in real time in their Tapfiliate dashboard.

Steps 1–5 happen during the landing-page request. Steps 6–8 happen when the order is confirmed. The buyer’s browser is involved only in step 1, when it passes the ref parameter in the URL.

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

With 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 results in lost attribution.

With server-side tracking, your backend is the source of truth. It captures the referral code on arrival, creates the click, stores the click ID, and fires the conversion event on purchase. The buyer’s browser settings are irrelevant, whether the issue is cookies, JavaScript blocking, or a web-versus-app environment.

Client-side (pixel)Server-side (S2S API)
Source of conversion signalBuyer’s browserYour backend server
Impact of ad blockersBlocks the tracking scriptNo impact
Impact of iOS ITPCookie expires after 24 hoursNo impact
Attribution accuracy on mobile60–70%Near-complete
Cross-device trackingNoYes, via session/account
Implementation complexityLowMedium
GDPR exposureHigher, with data stored in the browserLower, with server-controlled data

The Tapfiliate REST API: Your Implementation Toolkit

Authentication

Every request to the Tapfiliate REST API requires this header:

X-Api-Key: YOUR_API_KEY

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

The API key allows users to create conversions and approve commissions. An exposed key is a financial risk, not merely a tracking risk.

// Correct: environment variable
export TAPFILIATE_API_KEY="your_key_here"

// Wrong: hardcoded
const apiKey = "tap_live_abc123xyz"; // Do not do this

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

Here is a quick API key security checklist to review before writing any tracking code:

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

None of this is specific to Tapfiliate. It applies to every API key your backend manages. Affiliate platform keys are particularly sensitive, however, because they sit at the intersection of money and partner trust.

The three essential endpoints

For server-side affiliate tracking, you need two primary endpoints and one optional endpoint.

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

POST /conversions/
Records a conversion linked to a stored click ID. Tapfiliate attributes the conversion to the correct affiliate and queues the commission for payment. Send your internal order ID as external_id to prevent duplicates.

POST /customers/ (optional)
Creates a persistent customer record. This is relevant to subscription and SaaS programs that track customer lifetime value and need recurring commission attribution across multiple billing events, not just the first purchase.

Tapfiliate also supports secondary attribution using a referral code or coupon code directly in POST /conversions/. If a user arrives without a ref parameter but enters an affiliate coupon at checkout, you can send the coupon code in the conversion call and still attribute the conversion accurately.

Implementation Guide: Complete S2S Setup

This guide uses Node.js. The same pattern applies to Python, Go, Ruby, or any other backend language. Replace the fetch calls with your preferred HTTP client.

Step 1: Capture the affiliate ref parameter on arrival

Visitors may or may not arrive at your website with an affiliate referral code in the URL. Your landing-page middleware should check every request and create a click whenever the code is present.

// Express.js middleware, runs on every incoming page request
app.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 block is intentional and crucial. Click creation should never block the page from loading. If the Tapfiliate API is slow or temporarily unavailable, the user should still be able to access your site. Log the failure and continue.

Step 2: Store the click ID on the server

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 the server-side session, in a signed cookie with the HttpOnly and Secure flags enabled, or in a database row linked to the user’s session identifier.

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

Step 3: Fire the conversion postback

When a purchase is completed, call POST /conversions/ from your backend order handler. Never call it 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
  }
}

Connect this to your post-payment webhook or order confirmation handler:

// Post-payment webhook endpoint
app.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 most common implementation mistake is firing the conversion call on the frontend “thank you” page through a script or GTM trigger. If the buyer closes the tab before the page fully loads, which happens more often than you might think, the conversion is silently lost. Always fire the call from the backend payment confirmation event.

Step 4: Verify the conversion in the Tapfiliate dashboard

After your first test transaction, log in to your Tapfiliate account and open Conversions. Confirm that the conversion appears with the correct affiliate, order value, and “Pending” status.

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

Edge Cases Developers Face

Missing click IDs: direct traffic and dark social

Not every visitor arrives through an affiliate link. Direct traffic, email newsletters, SMS campaigns, and dark-social shares do not contain a ref parameter. Your middleware finds nothing, no click ID is stored, and no affiliate is recorded.

I have noticed that teams often forget to handle this case explicitly. Null click IDs sent to POST /conversions/ produce 400 errors that look like integration failures. They are not. They are expected situations that require a conditional branch.

// In your payment confirmation handler
if (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 arrive without a ref parameter but use an affiliate coupon at checkout, you can send the coupon code directly in the conversion call instead of a click ID. Tapfiliate resolves the attribution from the coupon.

Preventing duplicate conversions

Network delays cause webhook retries. Without deduplication, a single purchase may fire two or three conversion events. Your affiliate program then credits the same sale multiple times.

The solution is the external_id field in 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 new ID for each retry. A stable order ID makes the endpoint idempotent. You can retry as many times as necessary without creating duplicate commissions.

Multi-touch and cross-session attribution

A user often interacts with several affiliate links before converting. They click Affiliate A’s Instagram post on Monday. They click Affiliate B’s blog link on Thursday. They make a purchase on Friday.

Tapfiliate’s default model attributes the commission to the last click, which is Affiliate B. If your program uses first-touch attribution, keep 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 session
if (!req.session.tapClickId) {
  const click = await createTapfiliateClick(affiliateRef);
  req.session.tapClickId = click.id;
}

The model you use depends on your program rules. The implementation is identical in both cases. The only difference is whether you overwrite the stored click ID during subsequent visits through affiliate links.

Privacy, Security, and GDPR

Never expose your API key on the client side

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

If the key appears in a browser network request, whether through a React component, a GTM custom HTML tag, or a Segment source function, any user can extract it using browser developer tools. They could then generate fraudulent conversions for any affiliate in your program.

Every Tapfiliate REST API call should come from your backend. Your frontend JavaScript has no legitimate reason to call the Tapfiliate API. If you use a client-side tag manager as part of your S2S implementation, make sure the tag manager integration uses a separate, isolated data layer. Never pass your API key through it.

GDPR and data minimization for click IDs

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

Practical compliance measures include:

  1. Set a retention limit. Tapfiliate’s default attribution window is 30 days. Delete click IDs stored in your session or database after 30 days without a conversion.
  2. Avoid unnecessary linking. Do not store click IDs in the same database row as directly identifying data such as a name or email address without a documented legal 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 should include the removal of stored click IDs associated with the user.

For programs with EU users, document click ID processing in your Records of Processing Activities. For a deeper look at S2S integration for influencer program tracking, read our guide to hybrid influencer tracking with affiliate software.

Testing Your S2S Implementation

Parallel tracking validation

Before disabling your existing pixel tracking, run both systems in parallel for at least 48 hours. This lets you compare conversion counts and identify click ID storage problems without losing attribution data.

Use this testing 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 number of conversions in Tapfiliate with the number in your order management system.
  4. If the counts match across all 10–20 test orders, S2S is working correctly. Disable the pixel.
  5. If they do not match, check the click ID retrieval logic in the payment handler first. The most common cause is the session expiring between the initial visit and the purchase for orders with a longer consideration period.

One practical way to test without placing real orders is to use a Tapfiliate test affiliate account and a staging environment. Create a test affiliate, click your own affiliate link in an incognito window, make a test purchase, and then verify that the conversion appears in the Tapfiliate dashboard. This gives you a complete end-to-end test without affecting production data.

One final point to verify: make sure your external_id deduplication works. Submit the same test order ID twice and confirm that the second call returns a 409 status code or a similar duplicate rejection. If both calls succeed, deduplication is not working and you will accumulate duplicate commissions when webhooks retry.

Tapfiliate’s conversion list shows the attribution source for every record. Filter by date range and confirm that conversions were sent through the S2S API. If conversions attributed through the JavaScript path appear during parallel testing, your S2S middleware is not capturing the click ID correctly on the landing page.

For larger programs migrating from another affiliate platform, 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, rather than relying on a pixel fired in the buyer’s browser. Because the conversion signal comes from your server, it is unaffected by ad blockers, iOS cookie restrictions, or the buyer leaving the page 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 makes a purchase, 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 referral code from the URL, makes a POST /clicks/ call, and receives the click ID in response. You store it on the server. At purchase, you send it with the conversion event, where it acts as the proof connecting a specific click to a specific sale without using cookies.

How do you implement cookie-free S2S tracking?

Capture the ref parameter from the URL on the user’s first landing-page request. Make a POST /clicks/ call with the referral code and store the returned click ID in the server-side session or database. When the user converts, make a POST /conversions/ call with the stored click ID from your backend payment handler. No cookie is used at any stage.

See the Tapfiliate REST API documentation for the complete parameter and response schema reference.

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 the identifiers you store, how long you retain them, and the legal basis for processing them. You should document click ID processing in your Records of Processing Activities, honor deletion requests, and apply data minimization.

Compared with pixel tracking, server-side tracking generally reduces GDPR exposure because the browser is no longer the data collection point.

Close the Gap

Your order management system already holds the truth. Every confirmed purchase is recorded there.

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

Three API steps separate a 65% attribution rate from near-complete attribution: POST /clicks/ on the landing page, a click ID stored in the session, and POST /conversions/ when the purchase is confirmed.

The affiliates who generated 847 of those 1,203 sales deserve accurate attribution for all 847. Your program deserves the data it needs to identify which traffic sources truly convert and which do not.

Start your free Tapfiliate trial and connect your backend directly to the Tapfiliate REST API. No credit card required. Trusted by more than 69,500 marketers and technical teams.

In this article

Why Pixels Fail (and Why Developers Get Blamed)

How Server-Side Affiliate Tracking Works

The Tapfiliate REST API: Your Implementation Toolkit

Implementation Guide: Complete S2S Setup

Edge Cases Developers Face

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.