Key takeaways

  • A postback URL is an address on your server that a platform calls to report a conversion, with the details in the query string.
  • Macros such as {user_id}, {status}, and {reward} in the URL you enter are replaced with real values on every call.
  • Verify the signature, store the last status of each transaction, and skip only repeated events, never later status changes.

What is a postback URL?

A postback URL is an address on your server that another platform calls when a conversion happens or changes. The call carries the details of that conversion in the URL, such as which user completed it, what it paid, its status, and a transaction ID. Your server reads those details, updates its records, and answers that it received the call.

Postbacks are the receiving end of server-to-server (S2S) tracking: the call goes directly from one server to another, so ad blockers, cookie settings, and closed browser tabs do not stop it. Offerwalls, affiliate networks, mobile measurement tools, and ad platforms all use them. This guide covers the URL itself: what goes in it, what arrives, and how to handle it.

What a postback URL looks like

You enter a postback URL once, in the platform’s dashboard, with placeholders called macros in curly brackets:

https://example.com/postback?user={user_id}&tx={transaction_id}&status={status}&reward={reward}&hash={hash}

When a conversion happens, the platform replaces every macro with a real value and calls the address, so your server receives something like:

https://example.com/postback?user=4821&tx=9f3a71c2&status=1&reward=150&hash=5b1e...

The parameter names on the left (user, tx) are yours to choose. The macro names on the right must match the platform’s list exactly.

How a postback reaches your server, step by step

  1. The click carries an ID. When a user opens an offer, the link includes your user ID or a click ID, for example ?user_id=4821.
  2. The platform stores it with the click.
  3. The user converts. They install the app, sign up, or finish the task.
  4. The postback fires. The platform calls your postback URL with the user ID, the amount, the status, and a transaction ID filled in.
  5. Your server checks and acts. It verifies the request, acts on the status, and answers with HTTP 200.
  6. Later updates follow. If the result was pending, or a credited result is reversed later, another postback arrives for the same transaction with a new status.

Common postback macros

Every platform names its macros a little differently, but most offer the same kinds of values:

  • User ID or sub ID: the ID you passed in the link, so you know whom to credit.
  • Click ID: a unique ID for the click, used by affiliate networks and trackers.
  • Transaction ID: the ID of one conversion. On many platforms it stays the same for every update of that conversion, from pending to credited, rejected, or reversed. On Moonetix, the credit and any later reversal share one transaction ID.
  • Event ID: a unique ID for each single call, useful for logging and for spotting a call that was delivered twice.
  • Status: what happened, for example pending, credited, rejected, or reversed.
  • Payout: what you earn for the conversion, usually in US dollars.
  • Reward: the amount in your own currency, such as coins or points, already converted.
  • Offer ID, name, and type: which offer was completed.
  • Country, device, and IP: where the user was, useful for reports and fraud checks.
  • Signature or hash: proof that the call really came from the platform and that the key values were not changed.

Handle every status, not only the first

A conversion is not always final the moment it is reported. It can wait for a review, be approved or rejected, and sometimes be taken back after it was approved. Platforms that send one postback per status change use the same transaction ID for all of them, so your script must look at the status, not only at the ID:

  • Credited: add the reward to the user.
  • Reversed: take the reward back, but only if you credited that transaction before.
  • Pending: never credit. Show the reward as pending if you like.
  • Rejected: never credit. The result pays nothing.

The safe pattern is to store the last status you applied for each transaction ID. When a call arrives, compare it with what you stored: a new status moves the transaction forward, while a repeat of an event you already applied is skipped. A reversal for a transaction you credited must never be skipped just because you have seen its transaction ID before.

Is a postback URL secret?

No. Anyone who finds the address can call it, so your script must never credit a reward just because a request arrived. The basics:

  • Verify the signature. Most platforms sign each postback with a key only you and they know, usually with HMAC-SHA256. Recalculate it on your server and compare in constant time.
  • Skip repeats. Platforms retry calls, and a retry must never pay twice.
  • Use HTTPS, so values cannot be read or changed on the way.
  • Treat IP allow-lists as an extra, never as a replacement for the signature.

Postback security walks through the threats and gives a complete PHP and Node.js handler with row locking, built on the Moonetix signature.

Chargebacks and reversals

Some conversions are reversed later, for example when an advertiser finds fraud or a purchase is refunded. Platforms report this with a second postback for the same transaction ID, marked by a status value. Some older platforms send a negative amount instead, but many, including Moonetix, always send positive amounts and let the status tell you whether to add or take back. Remove the reward you gave for that transaction, once.

If the user has already spent or withdrawn the reward, their balance goes below zero. That is why many rewards sites hold new earnings for a while before they can be cashed out. Publish the hold times on your site so users know how long each kind of offer is held.

Why postbacks go missing

When conversions do not appear, the cause is usually one of these:

  1. Macros were not replaced. You see {user_id} literally in your log, which means the macro name is wrong for that platform.
  2. A firewall blocked the call. Bot protection and security rules often block server requests. Allow your postback path or the platform’s IPs.
  3. A redirect dropped the values. An http to https or www redirect can lose the query string, and some platforms do not follow redirects at all. Enter the final address.
  4. Your script returned an error or answered too slowly, so the platform marked the postback as failed.
  5. The user ID never reached the link, so the platform had nobody to report.
  6. The signature did not match because a value was changed or decoded differently before hashing, or the wrong key was used.

Log every call with the time, the full query, and your answer. When a user says a reward is missing, the log shows at once whether the postback arrived. Some problems start on the user’s side, when ad blockers, VPNs, or strict privacy settings break the click before the conversion. A short help page that explains these to your users saves a lot of support tickets.

Postback URLs on Moonetix

On Moonetix, every publisher app has its own postback URL and postback secret key. You enter the URL in your dashboard under Apps, your app, Postback, where the five macros you need are listed first. We call your URL with a GET request for every event of every transaction:

  • {user_id}: the user ID you put in the offerwall link.
  • {transaction_id}: the same for the credit and any later reversal of one transaction.
  • {status}: 1 credited or 2 reversed. {state} gives the same in words: credited or reversed.
  • {reward}: the reward in your currency, after your conversion rate, user split, bonus, and rounding. {payout} is what you earn in US dollars. Both are never negative.
  • {hash}: HMAC-SHA256, as lowercase hex, of transaction_id:user_id:reward:status, signed with your postback secret key.

A result is credited as soon as the advertiser confirms it, so there is no pending postback to handle. Answer with any 2xx code within 6 seconds; we do not follow redirects. Failed calls are retried up to 5 times, every attempt is listed under Reports, Postbacks, and a failed call can be sent again from there, as How to resend a failed postback shows. The Send a test postback form lets you check your handler before going live. The full reference is in our docs: Postbacks, Credits and reversals, and Testing and troubleshooting.

Signed postbacks for every status on the Moonetix offerwall

If you run a rewards site or app, postbacks are how your users get paid, so it helps when the platform makes them easy to trust. Every Moonetix app gets its own postback secret key, signed postbacks for both statuses, credited and reversed, and a Postbacks report in your dashboard. Publisher applications are open, and you can build your handler against the docs now. See how the Moonetix offerwall works.

Frequently asked questions

Is a postback the same as a webhook?

A postback is a kind of webhook. The term is used in advertising and affiliate marketing for webhooks that report conversions.

What is the difference between a postback URL and a callback URL?

Many platforms use the two terms for the same thing: the address on your server that they call when something happens.

Why do I receive the same postback twice?

Usually because the platform retried after a slow answer or an error. Store the last status you applied for each transaction ID and skip an event you already processed.

Why did I get a second postback with the same transaction ID?

Because the conversion changed. A pending result can become credited or rejected, and a credited result can be reversed later. Process the new status instead of ignoring it.

Do I need a postback if I already use a pixel?

For apps, offerwalls, and rewards sites, yes. A postback keeps working when the browser blocks the pixel, and it is the only reliable way to credit users in your own currency.