Postback URL explained: how it differs from a pixel and a webhook

A postback URL is the address your server calls to report a sale with its click ID. How it differs from pixels and webhooks, with a 6-step Stripe setup.

Muzahid Maruf — Founder of TrackRev.io

Muzahid MarufUpdated

Affiliate programs · 8 min read
On this page
  1. 01What does a postback URL look like?
  2. 02How does pixel tracking work and where does it lose sales?
  3. 03Postback URL vs pixel tracking in 6 scenarios
  4. 04How does a postback URL differ from a webhook?
  5. 05How do you send a postback from a Stripe sale?
  6. 06When is a pixel still the better tool?
  7. 07How does TrackRev record the conversion?

Explore with AI

Opens this article inside the chosen assistant with a ready-made prompt.

A postback URL is a callback link owned by the platform that sent you a click, such as an affiliate network or a tracker like Voluum.

After a sale, your server calls the link carrying the click ID and order value, and the platform logs the conversion without any browser.

A conversion pixel instead fires from the thank-you page and relies on a cookie to find the click. The difference matters most for subscriptions.

A thank-you page loads once, so a pixel on the page sees payment 1 of 12, while a server-side postback can report all 12 payments of a $49 plan.

The sections below compare 3 methods, including the Meta Pixel and Conversions API, and walk through a 6-step Stripe Checkout setup with code.

Key takeaways

  • A postback URL is an address on the platform that issued the click. After a sale, your server makes a request to it with the click ID and order value, with no browser involved.
  • A thank-you page pixel sees only the first payment of a 12-payment subscription, and Safari can expire its script-written cookie within 24 hours.
  • TrackRev has no postback URL setting. It reads Stripe, Paddle Billing, Polar, Lemon Squeezy, Creem and Dodo Payments charges, and sends signed sale.created webhooks.

What does a postback URL look like?

Voluum's documentation describes server-to-server (S2S) tracking as a server on the offer owner's side passing conversion data back to the platform the visitor came from.

In Voluum's version, cid is the only required parameter, and 9 optional ones follow: payout, txid, et, currency and param1 to param5. The txid value is a unique ID per conversion, so 1 visitor can produce several.

The round trip has 2 legs, shown in the code block. First the platform appends a unique click ID to the visitor's link. Then your server returns the ID with the order value after the sale.

For example, a $49.00 order for click 7f3a91c2 reports payout=49.00 and currency=usd. Reusing the txid on a resend lets the platform drop the duplicate, and the Stripe Session ID, such as cs_live_a1B2c3, makes a stable txid.

A postback round trip: the platform appends a unique click ID to the visitor's link, and your server returns that ID after the sale
1. The platform sends the visitor to your site with a click ID
   https://yoursite.com/pricing?clickid=7f3a91c2

2. Your server reports the paid order
   https://tracker.example.com/postback?cid=7f3a91c2&payout=49.00&currency=usd&txid=cs_live_a1B2c3

Sources: doc.voluum.com/article

How does pixel tracking work and where does it lose sales?

A conversion pixel is an image tag or script on the post-payment page.

Voluum's pixel documentation says the click ID sits in a browser cookie read by the thank-you page, the pixel stops working when cookies are disabled, and the pixel is the fallback for platforms without S2S postbacks.

More on the browser side in server-side vs client-side click tracking. A pixel works in 3 steps: the visitor clicks, the cookie stores the click ID, and the thank-you page fires the request. The pixel and the cookie fail separately.

A visitor can close the tab before the thank-you page loads, or a browser can delete the cookie first.

As of October 2026, the table lists how 6 browsers and blockers treat the cookie, and your loss depends on your browser mix.

Browser or blockerDefault behaviorSource
Apple SafariIntelligent Tracking Prevention blocks all third-party cookies, deletes script-written cookies after 7 days without interaction, and caps them at 24 hours when it detects link decoration such as a click ID in the URLWebKit
Mozilla FirefoxTracking protection has been on by default since 2019, and Total Cookie Protection keeps cookies in a separate jar per site for all desktop users since June 14, 2022Mozilla
Brave BrowserBlocks tracking cookies by defaultMDN
Microsoft EdgeBlocks trackers from unvisited sites and known harmful trackers by defaultMDN
Google ChromeAllows third-party cookies by default, and on April 22, 2025 Google kept that approach with no standalone promptGoogle
Ad blockersEasyList's EasyPrivacy list targets web bugs and tracking scripts, and EasyList's filter lists work with Adblock Plus, AdBlock, uBlock Origin and AdGuardEasyList

What browsers and blockers do to a cookie-based pixel match. Your loss depends on your browser mix.

Sources: doc.voluum.com/article

What a thank-you page pixel misses on renewals

A monthly subscriber loads the thank-you page once, so a pixel on it fires for the first payment only. Take a program that pays a 20% recurring commission. A $49 plan pays $117.60 over 12 months.

Plan pricePixel recordsPostback records over 12 months
$19$3.80$45.60
$39$7.80$93.60
$49$9.80$117.60
$99$19.80$237.60
$199$39.80$477.60

An illustration with made-up inputs: 12 monthly payments and a 20% recurring commission.

Measuring your own loss

Count the paid, non-refunded Stripe checkouts from the last 30 days that carried a click ID, then count what the platform logged. Suppose 120 checkouts earn a $9.80 commission each. A 10% undercount leaves $117.60 uncredited.

UndercountSales missedCommission uncredited
5%6$58.80
10%12$117.60
15%18$176.40
20%24$235.20
30%36$352.80
40%48$470.40

A second illustration with 120 checkouts, not a benchmark.

Postback URL vs pixel tracking in 6 scenarios

ScenarioPixelPostback URL
Safari buyer pays 2 days after clicking a link with a click IDThe script-written cookie can already be gone, capped at 24 hoursClick ID found in your database, $49.00 reported
Buyer closes the tab before the thank-you page loadsNothing fires$49.00 reported from the Stripe event
Buyer with uBlock Origin pays $49.00The script or request can be blockedNo browser request to block
ACH Direct Debit payment that fails after Stripe Checkout completesFires for $49.00 anywayWaits for the async success event, so a failure reports nothing
Payment 7 of 12 on a monthly planNo page view, $0The invoice event reports $9.80 commission
Refund of $49.00 after 20 daysNothing firesNeeds its own call, as in affiliate refund clawbacks

Rows 2 and 4 follow Stripe's fulfillment guide.

For a sale that a processor confirms, I treat the postback as the record and the pixel as optional. The 6 rows use a $49.00 sale.

How does a postback URL differ from a webhook?

Both are HTTP requests with event data. Ownership differs. Stripe picks the payload, signs it and retries failures (see Stripe's webhook documentation).

A postback is the receiver's endpoint, so the platform fixes the URL and parameters and you own the retries. A webhook flows from the platform to your server, usually as a JSON body with a signature header.

Stripe's libraries tolerate 5 minutes of clock skew by default, and Stripe retries for up to 3 days. A postback flows from your server to the platform, usually as query parameters.

You retry, because the platform never learns the call failed. Verification differs as well: a webhook carries a signature header, and a postback may rely on a secret token or an IP allowlist set by the platform.

TrackRev's outbound webhooks make 3 attempts inside a 10 second budget. Duplicates are handled differently too: dedupe a webhook on the event ID, and dedupe a postback on the transaction ID.

How do you send a postback from a Stripe sale?

The 6 steps use Stripe Checkout and run in order: save the click ID, pass the ID to Checkout, fire on paid, verify the signature, send the postback, and resend after downtime.

Paddle's route is custom data and Polar's route is checkout metadata (see Paddle checkout attribution with custom_data).

StepWhat to doLimit to design around
1Save the click ID in your database when the visitor landsSafari caps script-written cookies at 24 hours when it detects a click ID in the URL, and deletes them after 7 days idle
2Pass it to Checkout in client_reference_id200 characters, while metadata allows 50 keys with names up to 40 characters and values up to 500
3Fire when payment_status is paid on checkout.session.completed or checkout.session.async_payment_succeededA free trial reports paid with a $0 invoice, so require an amount above 0
4Verify Stripe-Signature, skip repeated event IDs, skip events where livemode is false, return a 2xx first5 minute tolerance, a 10 second redirect wait in hosted Checkout, 16 endpoints per account, TLS 1.2 or 1.3
5Send the click ID, amount, currency and a transaction ID such as the Session ID, with a 5 second timeout. Retry timeouts, 429 and 5xx answersOver 135 currencies, and most use 2 decimals
6If the platform was down, resend the event and reuse the transaction ID15 days in the Stripe Dashboard, 30 days with the Stripe CLI

Limits from Stripe's webhook, Checkout Session, session creation and metadata documentation.

For subscriptions, save the Stripe customer ID next to the click ID after the first sale, because renewals arrive as invoice.paid events without the session's reference. Alternatives are in Stripe metadata attribution setup.

Stripe amounts use the smallest unit, so 4900 USD is $49.00, while JPY and KRW have no minor unit. Dividing every amount by 100 is wrong for yen and won.

Pass the click ID in client_reference_id, which allows 200 characters, while metadata allows 50 keys. A free trial reports paid with a $0 invoice, so require an amount above 0.

Stripe amountCurrencyReal value
50USD$0.50, the minimum charge
4900USD$49.00
1099USD$10.99
2000EUR20.00 EUR
500JPY500 yen
5000KRW5000 won
2500GBP25.00 GBP
500ISK5 ISK
500UGX5 UGX
17500HUF175.00 HUF, the minimum charge
50JPY50 yen, the minimum charge

From Stripe's currency guide: amounts use the smallest unit. JPY and KRW have no minor unit, and ISK and UGX are zero-decimal but still sent in a two-decimal form, so dividing every amount by 100 is wrong for yen and won.

Webhook handler that fires the postback (Next.js route, Stripe Node library)
import Stripe from "stripe"; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);const ZERO_DECIMAL = new Set(["jpy", "krw"]); // check Stripe's currency list for the rest export async function POST(req: Request) {  const raw = await req.text(); // raw body, required for the signature check  const event = stripe.webhooks.constructEvent(    raw,    req.headers.get("stripe-signature")!,    process.env.STRIPE_WEBHOOK_SECRET!,  );   if (!event.livemode) return new Response("ignored test event");  if (await alreadyProcessed(event.id)) return new Response("duplicate");   if (    event.type === "checkout.session.completed" ||    event.type === "checkout.session.async_payment_succeeded"  ) {    const s = event.data.object;    const clickId = s.client_reference_id;    // For delayed methods, "completed" arrives unpaid and is skipped here.    if (s.payment_status === "paid" && clickId && (s.amount_total ?? 0) > 0) {      const cur = s.currency ?? "usd";      const amount = ZERO_DECIMAL.has(cur) ? s.amount_total! : s.amount_total! / 100;      await firePostback({ clickId, amount, currency: cur, txid: s.id });    }  }   await markProcessed(event.id);  return new Response("ok");} async function firePostback(p: { clickId: string; amount: number; currency: string; txid: string }) {  const url = new URL("https://tracker.example.com/postback");  url.search = new URLSearchParams({    cid: p.clickId,    payout: p.amount.toFixed(2),    currency: p.currency,    txid: p.txid,  }).toString();  const res = await fetch(url, { signal: AbortSignal.timeout(5000) });  if (!res.ok) throw new Error("postback failed with " + res.status); // Stripe retries the event}

When is a pixel still the better tool?

A pixel is the better tool when the receiving platform accepts nothing else. Some platforms accept only pixels, as Voluum notes for networks without S2S postbacks.

Ad platforms run both: Meta recommends the Meta Pixel plus the Conversions API, matched on event_id and deduplicated only when the second event arrives within 48 hours of the first (Meta's deduplication guide).

If an affiliate sale fires both, send the same transaction ID in each, or a $49 sale can show up as 2 conversions and $98 of revenue.

In practice, run both where an ad platform recommends both, and keep 1 transaction ID across both.

For example, a $49 sale reported by the Meta Pixel and by the Conversions API with 2 different IDs shows 2 conversions, while 1 shared event_id within 48 hours shows 1 conversion and $49.

How does TrackRev record the conversion?

TrackRev is SaaS affiliate software with link tracking and revenue attribution built in, and TrackRev needs no postback from you.

TrackRev reads the paid charge from Stripe, Paddle Billing, Polar, Lemon Squeezy, Creem or Dodo Payments and joins the charge to the earlier click.

We built TrackRev to record the click on the server at the redirect, with 0 scripts on your destination page.

The visitor ID lives in a first-party vid cookie with a lifetime of 1 year, and the checkout carries the ID in client_reference_id or metadata.

Charges sync hourly through a read-only key with 4 read scopes, or in seconds with 1 optional webhook scope for 3 events. The first sync pulls 30 days.

The join looks in 4 places, ending with the buyer's email, and outbound signed webhooks cover 7 event types, including sale.created.

What does TrackRev write when a sale lands?

On TrackRev's affiliate program, the commission row is written when an attributed sale lands, once per order and partner, and reverses on a refund. The pixel writes its cookie with JavaScript, so Safari's caps on script-written cookies apply to it.

The redirect re-sends the visitor ID on every link click, and email matching, after an identify call, covers purchases with no ID.

There is no postback URL setting: subscribe a webhook to sale.created and map the order or email to your stored click ID. If a network owns your click tracking, use the conversion setup of the network instead.

In our pricing for 2026, Indie costs $29 a month for 1 workspace and 1 tracked domain, and Growth costs $299 a year for up to 10 workspaces. We built the 2 paid plans so neither caps commission tracked.

The free plan covers 50 links and 1,000 tracked events a month.

PlanPriceWhat it covers here
Free$0Link tracking only: 50 links and 1,000 tracked events a month
Indie$29 a monthThe affiliate program for 1 workspace and 1 tracked domain, with no cap on commission tracked
Growth$299 a yearUp to 10 workspaces and tracked domains, unlimited team members, no cap on commission tracked

TrackRev plans, October 2026. Indie bills monthly only and Growth bills yearly only. See pricing for the full list and the first-party tracking page for the pixel.

Found this useful? Share it.

PostLinkedIn

Frequently asked questions

Muzahid Maruf — Founder of TrackRev.io

Written by

Muzahid Maruf

Founder, TrackRev.io & Contant.io

Muzahid Maruf founded TrackRev.io, SaaS affiliate software with no limit on tracked revenue, and Contant.io. He writes about affiliate programs.

Writes about Marketing attribution · Link tracking · Revenue analytics · SaaS growth

Run your affiliate program without a revenue cap.

Affiliate software for indie hackers and small SaaS teams: $29 a month flat, 0% fees, link tracking and revenue attribution built in. Free to start, no card.