An affiliate click leaves a strange gap in your data. Someone clicks your link, disappears onto the merchant's site, and — maybe — buys something three days later from a different device. The commission shows up in your network dashboard a week after that, as a lump sum with no memory of which of your pages sent the buyer. That gap between the click you can see and the commission you can't connect to it is the single reason most affiliate sites can't tell you their real earnings per page. Closing it is what conversion tracking does, and server-side, there are exactly two ways to do it: a postback, or an API.
The click-to-commission gap
Everything upstream of the click, you can measure yourself. Which page, which link, which placement — that's your side of the fence, and a good affiliate tracking setup captures all of it. Everything downstream — did they buy, how much, how much commission — happens on the merchant's servers, where you have no visibility at all.
The network is the only party that sees both sides: it knows the click came from you (because your ID rode along on the link) and it knows the sale happened (because the merchant told it). Conversion tracking is simply the pipe that carries that reconciled fact — this sale belongs to this click — from the network back to you. Get that pipe working and the click-to-commission gap closes; skip it and you're left doing what most affiliates do — reconciling a spreadsheet of network payouts against a guess about which pages earned them.
There are two ways to build the pipe. The difference is just who calls whom.
Postback: the network pushes the sale to you
A postback (also called a server-to-server or S2S postback) flips the usual direction: instead of you asking the network for data, the network calls you the instant a conversion is confirmed.
It works in three moves:
- You put a unique ID — your SubID — on every outbound affiliate link. This ID encodes what you care about: at minimum the page, ideally the placement too.
- You register a postback URL on your own server with the network, with placeholders for the values you want back.
- When the sale clears, the network fires that URL, substituting in your SubID and the commission. Your endpoint records it.
The registered URL looks like this — the {...} tokens are placeholders the network fills in:
https://track.yoursite.com/postback
?subid={SubId1}
&order_id={ActionId}
&amount={Payout}
&status={Status}
And the receiver is about as much code as it sounds — read the query, write the row:
// app/postback/route.ts (edge or node)
export async function GET(req: Request) {
const u = new URL(req.url);
const secret = u.searchParams.get("k");
if (secret !== process.env.POSTBACK_SECRET) return new Response("no", { status: 403 });
await recordConversion({
subid: u.searchParams.get("subid") ?? "", // carries the page
orderId: u.searchParams.get("order_id") ?? "",
amount: Number(u.searchParams.get("amount") ?? 0),
status: u.searchParams.get("status") ?? "", // pending | approved | reversed
});
return new Response("ok"); // networks expect a fast 200
}
On Impact specifically, you'll find this under your tracking settings as a server-to-server postback; you pass your value in SubId1 on the click, and Impact returns it (as SubId1) along with the ActionId, Payout, and action State when it fires the postback. Add a shared secret to the URL so nobody can forge conversions, and return a fast 200 — networks retry slow or failing endpoints and eventually give up.
The appeal is that it's real-time and per-sale. The moment a commission is registered, you know — no polling, no waiting for a report to refresh. Because it's a server-to-server call keyed on your SubID rather than a browser cookie, ad blockers and third-party-cookie loss don't touch it, which is the accuracy win server-side tracking is genuinely good for.
The catch: a postback captures a conversion at the status it had when it fired — usually "pending." Commissions get approved, reduced, or reversed weeks later, and the postback that told you about the sale won't tell you it was later refunded. Which is exactly what the second method is for.
API: you pull the sales from the network
An API reverses it again: your server calls the network on a schedule and pulls down the conversions itself.
// runs on a cron — hourly, or nightly for reconciliation
const res = await fetch(
"https://api.impact.com/Mediapartners/ACCOUNT_SID/Actions?" +
new URLSearchParams({ StartDate: since, PageSize: "1000" }),
{ headers: { Authorization: "Basic " + btoa(`${SID}:${TOKEN}`), Accept: "application/json" } }
);
const { Actions } = await res.json();
for (const a of Actions) {
await upsertConversion({
subid: a.SubId1, // same key as the postback
orderId: a.Id,
amount: Number(a.Payout),
status: a.State, // now the *current* status, not a snapshot
});
}
Because you control when it runs and it returns the current state of every action, an API pull is the tool for the jobs a postback can't do: backfilling history when you first set tracking up, reconciling pending commissions against their final approved or reversed amounts, and covering networks that don't offer postbacks at all. The trade-off is latency and effort — you only know as often as you poll, you have to store a cursor so you don't miss or double-count actions, and every network's API is its own little adventure in authentication and pagination.
Postback vs API, side by side
| Postback (push) | API (pull) | |
|---|---|---|
| Direction | Network → you | You → network |
| Timing | Real-time, per sale | Scheduled (hourly/nightly) |
| Best at | Live earnings, instant signal | Backfill, reconciliation, final status |
| Status accuracy | Snapshot at fire time (usually pending) | Current status every pull |
| Setup | Register one URL + a receiver | Auth, pagination, a cursor, a cron |
| Fails when | Your endpoint is down (retries, then lost) | You poll too rarely or miscount actions |
| Cookies? | Cookieless (S2S) | Cookieless (S2S) |
Read that table and the answer to "which one?" stops being either/or. Use both. Let postbacks give you the live number the moment a sale lands, and run an API sweep nightly to true everything up against final, locked earnings. Postback tells you something happened; the API tells you what it was ultimately worth.
"Do I just use wecantrack?"
Probably the most-searched version of this question pairs it with a tool — usually wecantrack — that does the postback-and-API plumbing across dozens of networks so you don't have to. Here's the honest take, because it's a real trade and not a gotcha.
A middleware like that genuinely saves you the per-network setup, and if you're running twenty networks that's a lot of little integrations to not maintain. But you're paying a subscription, and — the part worth thinking about for a data company's own site — your revenue data now flows through a third party's servers before it reaches you. If you run a handful of networks, pointing their postbacks straight at your own endpoint (the twenty lines above) keeps the whole loop first-party and the data unambiguously yours. If you run dozens, the fee may buy back more time than it costs. The mechanism underneath is identical either way; you're only deciding who operates the pipe.
The one thing both methods depend on
Notice what postback and API have in common: neither works without a unique SubID on the outbound link, and that SubID is only as useful as what you encode in it. A conversion that comes back tagged SubId1=xyz is worthless if xyz doesn't tell you which page and placement produced the click. Get this right — a consistent, parseable ID that carries the page — and both pipes light up. Get it wrong and you'll have perfectly plumbed tracking that reports total conversions and nothing about where they came from. This is the same discipline behind network tracking IDs and SubIDs in every tracking method; server-side just raises the stakes, because now it's the join key for your entire revenue dataset.
The payoff: per-page EPC you can trust
Here's why any of this is worth the trouble. Once conversions flow back keyed on a SubID that carries the page, you can finally join them to the clicks you already log — and the moment click data and commission data share a key, per-page numbers become real. Earnings by page. EPC by placement. Revenue per visitor by cluster. The metrics that tell you which content to double down on and which to cut — none of which GA4 can compute, because it never sees your commissions.
That's the whole point of closing the click-to-commission gap: not tidier tracking for its own sake, but an EPC number you'd actually bet a content decision on.
Related reading
- Server-Side Affiliate Tracking: What It Solves (and What It Doesn't) — the honest case for going server-side at all, before you build either pipe.
- How to Track Affiliate Links: Every Method Compared — where postback/API fit among GA4 events, plugins, and network IDs.
- Revenue Attribution for Affiliates — what to do with conversions once they're flowing back.