Product
Publishers
Advertisers
Networks
Help
Pause animations

Postbacks

Signed postbacks for each credit and reversal.

Send a test before traffic runs. If a delivery fails, Flight Recorder, our trace of every conversion, shows the status and the error.

A postback is a server-to-server request: when a conversion happens, we call a URL on your server so you can credit the user. What is a postback URL?

GET postback.pebble.example 200 OK

https://postback.pebble.example/afos?user=u_48213&txn=act_9f1c27e4&amount=4.2&action_id=act_9f1c27e4&sub_id=u_48213&offer_id=aff_7c1e40b2d9a3&status=completed&payout=4.2&user_payout=0&offer_name=Harbor+Blocks&clicktimestamp=2026-10-29T19%3A06%3A00Z&conversion_name=Install&goal_id=1001&offer_id_numeric=136469376326051&goal_id_numeric=140574789535191&is_offer_complete=false&sub1=home_tab&sub2=exp_42&verifier=16de60dc2fc2ff3e…

Your server/afos

  • verifier matches
  • act_9f1c27e4 · completed is new
  • Answered 200 OK
act_9f1c27e4 Harbor Blocks · Install Sent u_48213 $4.20 2:09 PM
Flight Recorder keeps every delivery with its status.

01Credit, credit, reverse

One action ID, from credit to reversal.

Harbor Blocks, three postbacks

Skip this animation Skip these steps
  1. 01 Install

    The install is credited

    Harbor Blocks pays you once, at install: payout=4.2, status=completed, under its own action_id.

    Pebble Rewards' postback log · u_48213
    1. 2:09 PM completed

      action_id=act_9f1c27e4 payout=4.2 user_payout=0 conversion_name=Install

      Credited the install

    Your earnings from u_48213 $4.20

  2. 02 Step

    A later step rewards the player

    Games paid per install pay you at install; later steps reward the player. Level 5 posts back with payout=0 and user_payout=0.35, so you can pay your user.

    Pebble Rewards' postback log · u_48213
    1. 2:09 PM completed

      action_id=act_9f1c27e4 payout=4.2 user_payout=0 conversion_name=Install

      Credited the install

    2. 2:31 PM completed

      action_id=act_2b84d1c0 payout=0 user_payout=0.35 conversion_name=Reach level 5

      Gave the player 350 pebbles

    Your earnings from u_48213 $4.20

  3. 03 Reversal

    The install is reversed

    Same action_id, status=reversed, the amount it takes back and a reason in plain words when we recorded one.

    Pebble Rewards' postback log · u_48213
    1. 2:09 PM completed

      action_id=act_9f1c27e4 payout=4.2 user_payout=0 conversion_name=Install

      Credited the install

    2. 2:31 PM completed

      action_id=act_2b84d1c0 payout=0 user_payout=0.35 conversion_name=Reach level 5

      Gave the player 350 pebbles

    3. 3 days later reversed

      action_id=act_9f1c27e4 payout=4.2 user_payout=0 conversion_name=Install reason=IP mismatch / VPN or proxy detected

      Took back the install

    Your earnings from u_48213 $0.00

  4. 04 Your server

    Dedupe on action_id and status

    Key on both. On action_id alone the reversal looks like a repeat and gets dropped.

    Pebble Rewards' postback log · u_48213

    Key on action_id and status

    • act_9f1c27e4 · completednew
    • act_2b84d1c0 · completednew
    • act_9f1c27e4 · reversednew

    All three handled. Earnings from u_48213: $0.00.

    Key on action_id alone

    • act_9f1c27e4new
    • act_2b84d1c0new
    • act_9f1c27e4seen, dropped

    The reversal is lost. Your books still say $4.20.

Step 1 of 4

02Why we sign

Anyone who finds your URL could create rewards.

The Signature. A code made with your postback secret and sent with each postback, so your server can check it came from us.What is a signature? stops that. Every postback carries verifier, an HMAC-SHA256 of the full URL keyed with your ps_ secret. Without a secret we don't send, and your Postbacks page says so.

  1. 01

    Take the full URL your server received and remove verifier.

  2. 02

    Compute HMAC-SHA256 of the rest with your secret, as hex.

  3. 03

    Compare in constant time. Credit on a match; reject anything else.

Signature matches. This came from us and nothing in it changed.

Runs in your browser. Nothing is sent.

import crypto from "node:crypto";

// fullUrl: the URL as received, https included.
export function verifyPostback(fullUrl, secret) {
  const url = new URL(fullUrl);
  const received = url.searchParams.get("verifier") ?? "";
  url.searchParams.delete("verifier");
  const expected = crypto
    .createHmac("sha256", secret)
    .update(url.toString())
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(received);
  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(a, b);
}

Networks: postbacks are signed once you create a secret.

03Macros

Every value you can receive.

Put Macro. A placeholder in your postback URL, such as {sub_id}, that we replace with the real value.What is a macro? anywhere in your postback URL and we fill them in. We also add the standard parameters, then sign the whole URL.

Event

What we sendyour macros we add these

https://postback.pebble.example/afos?user=u_48213&txn=act_9f1c27e4&amount=4.2&action_id=act_9f1c27e4&sub_id=u_48213&offer_id=aff_7c1e40b2d9a3&status=completed&payout=4.2&user_payout=0&offer_name=Harbor+Blocks&clicktimestamp=2026-10-29T19%3A06%3A00Z&conversion_name=Install&goal_id=1001&offer_id_numeric=136469376326051&goal_id_numeric=140574789535191&is_offer_complete=false&sub1=home_tab&sub2=exp_42&verifier=16de60dc2fc2ff3e68aff478b6b73738a3e266064a7ab9c32967f30f76fcfec1

Runs in your browser. Nothing is sent. Signed with the example secret.

Postback macros: name, what it is and an example value
MacroWhat it isExample
The user ID you put on the tracking link. u_48213
The conversion's action_id. A reversal repeats it. act_9f1c27e4
What you earn for this step, in USD. On the Hosted Offerwall, the player's reward. 4.2
Your earnings. Same as payout, except on the Hosted Offerwall. 4.2
The player's reward for this step. Empty when none is set. 0.35
Our offer ID, the off_id on your link. aff_7c1e40b2d9a3
The step's ID. Empty on an offer with no steps. 1001
The offer ID as a whole number, up to 15 digits. Store it as a 64-bit integer. 136469376326051
The step's ID as a whole number, up to 15 digits, unique across offers. Store it as a 64-bit integer. 140574789535191
The step's name, not the offer's title. Install
The offer's title. Store it: a pulled offer can't be looked up later. Harbor Blocks
Either completed or reversed. completed
Why a reversal happened, when we recorded why. Empty on credits. IP mismatch / VPN or proxy detected
One of purchase, deposit or subscription, when the step is one. Empty otherwise. Macro only, never appended. purchase
The step's price in USD as a plain number, when its name states exactly one price. Empty otherwise. Macro only. 9.99
Your own values from the tracking link. Empty when the link carried none. home_tab

Matching ignores case, so {SUB_ID} works too. A macro with no value becomes empty, never braces. Anything else in braces stays in the URL, encoded: {nope} arrives as %7Bnope%7D.

04When something goes wrong

When a delivery fails, you see why.

Flight Recorder traces every conversion from the tap to your server and records each delivery with its result.

Conversion trace

act_9f1c27e4

failed

Timeline

  1. Click observed ok

    A matching click was found for this offer and sub ID.

    10/29/2026, 2:06:00 PM

  2. Conversion received ok

    Conversion recorded for Install; status is completed.

    10/29/2026, 2:09:00 PM

  3. Payout calculated ok

    Your payout is $4.20.

    10/29/2026, 2:09:00 PM

  4. Postback delivery failed

    connect ECONNREFUSED 203.0.113.5:443

    10/29/2026, 2:09:01 PM

What you see What to do
Failed: No active partner postback secret configured; postback not sent Your account had no active secret at send time, so nothing went out. Create a secret on your Postbacks page. Conversions after that go out signed. Outside the Hosted Offerwall, our team can resend the ones that failed.
Failed: a timeout, a refused connection or a 5xx We tried up to 3 times within 20 seconds and your server didn't answer in time, or answered with an error. Get your endpoint answering, then send a test postback. Outside the Hosted Offerwall, our team can resend a failed credit under its original action_id.
Failed: a 4xx such as 400 or 404 Your server refused it, so we don't retry. Only a 429 is tried again. Check the path and your signature check, and answer with a 2xx.
Skipped no url None of your saved postbacks applies to this conversion. Add a Conversion postback on your Postbacks page. Event postbacks never cover an ordinary conversion such as an install. Outside the Hosted Offerwall, our team can then resend the skipped credit.
A reversal never arrived We send a reversal only for a credit we delivered. If the credit failed, there is nothing to take back. Fix the credit's delivery first.

Promises

Three rules our sender keeps.

  1. 01

    We never send a publisher an unsigned postback.

    No secret means no send, and your Postbacks page tells you.

  2. 02

    Every reversal repeats the action ID it takes back.

    With status=reversed and the amount, so your books can match it.

  3. 03

    Every delivery is on record.

    Its status, your server's HTTP answer and the error, in Flight Recorder.

Questions

What developers ask about postbacks.

GET or POST?

GET. Any 2xx answer counts as delivered; we recommend 200.

Do postbacks arrive in order?

Don't rely on it. We send a reversal only after its credit was delivered, but key on action_id and status together rather than on arrival order.

What happens when my server is down?

We send up to 3 times, all within 20 seconds. A refused connection, a 5xx or a 429 is tried again after a short pause; a server that hasn't answered by the time limit, or any other 4xx, is not. Then the delivery is recorded as failed, with the error, in Flight Recorder. Outside the Hosted Offerwall, our team can resend a failed credit under its original action_id.

What happens when I rotate my secret?

The new secret takes effect at once and the old one stops working, so rotate when you can update your server straight away.

How do I test before sending traffic?

Use Send test postback on your Postbacks page. It goes through the same macros, parameters and signing as a real conversion, with sample values: sub_id=test_user_123, payout=1, an action_id starting test_ and test=true. It carries none of your sub1 to sub10, since no click is behind it. No conversion or balance is created.

I run a network. Are my postbacks signed?

Once you create a secret. Until then, network postbacks go out unsigned, so create one before you send traffic.

Send a test before your first click.

Sign-up is by request. We reply by email.