TapBaton
Start free
All articles
Conversions8 min read

What a postback URL is, and how to build one

A postback URL reports a conversion server to server. The three fields that matter, why macros differ by partner, and where to look when nothing arrives.

Two offset relay mechanisms passing a single orange signal between them

A postback URL is an address on your tracker’s side that a partner or store calls server to server when a conversion happens. It carries the click identifier, the conversion status and the amount, which is why it still reports a result where a browser pixel sees nothing: a different device, a later session, an in-app browser that kept no storage, a payment confirmed hours later.

None of it is magic. A postback works because two systems agreed in advance on one identifier and on a few parameter names. Most of the setup is that agreement, and most of the failures are a place where it quietly broke.

What follows: the three fields that matter, why macro syntax belongs to each partner, and where to look when nothing arrives.

Key takeaways

  • A postback URL is a tracker-side address a partner calls server to server when a conversion happens.
  • Three fields do the work: the click identifier, the conversion status and the payout amount.
  • Macro syntax is a contract with one partner, not a shared convention; take each token from their documentation.
  • Attempted is not confirmed, and an accepted request is not attribution.
  • Retries and duplicates are ordinary here; decide your repeat policy before launch.

The one-sentence definition, and the flow it describes

Read the definition as a sequence rather than a fact. Four things happen in order, each checkable separately.

  1. A visitor clicks your link. Your tracker records the visit and assigns a click identifier.
  2. You hand it to the partner in the offer link, using a parameter they support, and they store it against that visitor.
  3. A conversion happens on their side: a registration, an approved lead, a paid order.
  4. The partner calls your postback URL, returning the stored click ID with a status and an amount.

Step four is the postback; everything before it decides whether it can succeed, which is why a postback that never arrives is so often a step-two problem.

The three fields that always matter: click ID, status and payout

Trackers accept many parameters. Three do the work.

The click identifier is the only thing connecting a conversion to a visit, and it makes a round trip: out in the offer link, back in the postback. If it arrives empty, holds an unresolved macro, or holds a value your tracker never issued, there is nothing to attach the conversion to. Keitaro’s troubleshooting page lists the network not transmitting the real click identifier among its reasons a conversion is not registered.

The status says what happened: a lead, a sale, an approval, a rejection. It works only if the value the partner sends is one your tracker already knows. That page lists a missing status and an unrecognised status as separate failure causes; the two vocabularies need reconciling.

The payout is an amount, and an amount needs a currency and an owner. Decide whether the number is your commission or the customer’s order value and label it accordingly. If the partner cannot send a currency, set it as a literal value.

Macros are a contract with one partner, not a standard

A macro is a placeholder the sender replaces at call time: {clickid} in a saved template becomes a literal value in the request that goes out.

What matters is who owns which half. The parameter names belong to the receiver: your tracker decides whether the click ID arrives as click_id, cnv_id or cid. The macro tokens belong to the sender: the network decides whether its click value is written {clickid}, {subid} or #s2#. Neither side can guess the other’s half.

Published formats differ accordingly. Keitaro shows a {subid} placeholder beside plain status, payout and currency parameters. Binom uses {clickid}, with postback parameters cnv_id, payout and cnv_status filled by the network’s own {sub1}, {sum} and {status} tokens — and states plainly that every network has its own. Voluum shows {clickid} in the offer URL and hash-delimited tokens such as #s2# and #price#.

Treat any macro you did not read in your partner’s documentation as a guess, and check its spelling, including case. A macro that reaches the wire percent-encoded, as %7Bclickid%7D, is no longer a token to the sender.

A worked example you can copy

Here is the shape, using placeholder tokens. Neither the parameter names nor the macros are a format any tracker or network guarantees; replace both halves with what your two systems document.

https://tracker.example.com/postback?click_id={clickid}&status={status}&payout={payout}&currency=USD

Four separate decisions:

Part Who decides it What to verify
https://tracker.example.com/postback Your tracker The receiving path, plus any key it requires
click_id=, status=, payout= Your tracker The parameter names the receiver expects
{clickid}, {status}, {payout} The sending partner The tokens that partner documents
currency=USD You A literal value, if the sender has no macro

The example has a second half: the offer link you give the partner must carry your click ID into their system, in whichever parameter they support.

https://offer.example.com/signup?aff_sub={your_tracker_click_id_macro}

Here the roles swap — the parameter name is the partner’s, the macro is your tracker’s. Get this wrong and the postback arrives perfectly formed with nothing usable inside it.

Assemble your own template with your network’s macros in the Postback Builder: paste the receiving endpoint, add a row per parameter name and value, and it builds the URL, preserving braces, brackets and percent-wrapped tokens while URL-encoding ordinary text. Nothing is sent; what you copy is a template for your network’s postback field.

Testing without polluting live data

Test with a real click and a fake conversion, not the other way round.

Generate a click through your own link, then read the literal click identifier your tracker assigned it. That literal value, not {clickid}, belongs in a manual test call; an unresolved macro is a common reason a test looks like it worked and changed nothing. The Postback Inspector checks a URL for unresolved macros, repeated parameters, empty values and a missing click ID parameter. It sends nothing, so it confirms the URL is well formed, not that a conversion arrived.

Where the partner offers a test conversion, prefer it to a hand-built request. Then test the awkward cases on purpose: an unrecognised status, a click ID that was never issued, the same conversion sent twice. Decide the repeat policy before launch.

Duplicates are not hypothetical. Retries are ordinary in this class of integration, and Stripe’s webhook documentation is a useful published reference: automatic retries with backoff, no guaranteed ordering, and duplicate deliveries that receivers are expected to identify by event ID rather than timestamp. Those are Stripe’s characteristics for its own webhooks, not a description of your network.

Keep the outcomes separate. A request that was sent is not one that arrived. A request that arrived and returned a success response is not a conversion matched to a click. A matched conversion is not credit in an ad platform’s report. Attempted is not confirmed, and accepted is not attributed.

When it does not fire: the four places to look

Work along the chain in order; each place has a different fix.

1. The click ID never reached the partner. Check the offer link you handed over, including any redirect between your link and theirs, and confirm the value lands in their report.

2. The partner never called. Confirm a postback URL is saved in their account, on the offer you are testing. Keitaro lists a postback never configured in the network among its causes.

3. The call arrived and the receiver rejected it. Here live the documented receiver-side causes: a missing status parameter, a status the tracker does not know, a click identifier it never issued, an incorrect postback key. Read the tracker’s postback log before theorising.

4. The call was accepted but the conversion is not where you expected. A status may map to a different conversion type, a repeat may have been suppressed by your deduplication policy, or it may sit outside the window you are reading. Nothing is missing; something is classified differently than you assumed.

If you cannot tell which of the four you are in, add the logging that would have told you.

What differs between trackers — and why you still need one map

The three shapes above are three separate contracts, not variations on one format: a {subid} placeholder in one product, {clickid} with cnv_id and cnv_status parameters in another, hash-delimited tokens in a third. Copying a template from one into another is how most malformed postbacks begin.

The map you need is small. For each tracker-and-partner pairing, write down the receiving endpoint, the receiver’s parameter names, the sender’s macros, how the status values map, how currency is supplied, how to send a test conversion, and the date you last checked those pages.

Documentation changes and networks rename tokens; a dated map answers why a payout column is empty in a minute.

Frequently asked questions

What is a postback URL in affiliate marketing?

It is an address on your tracker that the network calls server to server when a conversion happens, returning the click identifier it was given earlier with a status and an amount.

Why is my postback not firing?

Work the chain in order: the click ID never reached the partner, the partner never called, the call arrived and was rejected for a missing or unknown status, or it was accepted and classified differently than you expected.

Does a success response mean the conversion was counted?

No. A success response says the request was received. Whether the tracker matched it to a click, and whether an ad platform later credits it to a campaign, are separate outcomes.