SharklioDocs Log in

DocsIntegration

Postbacks

One URL on your server receives every event: credits, pending results, rejections, and reversals. Here is what we send and how to handle it.

Setup

Add your postback URL in your dashboard under Apps, your app, Postback. We call it with a GET request for every event of every transaction, and we fill in the macros you put in the URL:

https://yoursite.com/postback?user={user_id}&tx={transaction_id}&event={event_id}&status={status}&reward={reward}&payout={payout}&hash={hash}

Use the macros you need, in any order and with any parameter names. Values are URL-encoded.

Macros

MacroDescription
{user_id}The user ID you put in the offerwall link.
{transaction_id}Stays the same for the whole life of a transaction: pending, credited, rejected, and reversed events all carry the same value.
{event_id}Unique for every event we send. Useful for logging.
{status}What happened, as a number: 1, 2, 3, or 4. See Statuses.
{state}The same as {status} in words: credited, reversed, pending, or rejected.
{reward}The reward in your currency, after your conversion rate, user split, and rounding. Never negative.
{currency}The name of your currency, for example Coins.
{payout}What you earn for the transaction, in US dollars. Never negative.
{offer_id}The ID of the offer.
{offer_name}The name of the offer as your user saw it.
{offer_type}click, task, or offer. See Offer lifecycle. Every type uses the same statuses.
{reason}For rejections and reversals, a short reason. Empty otherwise.
{country}Two-letter country code of the user.
{device}desktop, android, or ios.
{ip}The IP address the user completed the offer from.
{hash}Signature to check that the call comes from us. See Verify the hash.

Statuses

StatusStateWhat to do
1creditedAdd {reward} to the user.
2reversedTake {reward} back from the user, if you credited this transaction before.
3pendingDo not credit. Show the reward as pending. A status 1 or 4 follows later. Only sent if you turned pending events on in the Postback tab.
4rejectedDo not credit. {reward} and {payout} are 0.

Rules that keep your balances right:

  • Credit only on status 1, and only once per transaction_id.
  • A reversal (status 2) always carries the transaction_id of the credit it cancels. If you never credited that transaction, ignore it.
  • Amounts are never negative. The status tells you whether to add or take back.
  • The same event can arrive twice, for example after a retry. Store transaction_id with the last status you processed and skip repeats.

Verify the hash

{hash} is the HMAC-SHA256, as lowercase hex, of the transaction ID, user ID, reward, and status joined with colons, signed with your postback secret key from the Integration tab. Use the values exactly as they arrive.

$expected = hash_hmac('sha256', $tx . ':' . $userId . ':' . $reward . ':' . $status, $postbackSecretKey);
if (!hash_equals($expected, $hash)) {
    http_response_code(403);
    exit;
}

The postback secret key is different from the link hash salt. If you also want to allow-list IP addresses, ask [email protected] for the addresses we send from.

Responses and retries

  • Answer with any 2xx status code within 6 seconds. We do not read the response body and we do not follow redirects.
  • If your server fails or times out, we try again up to the number of retries you chose in the Postback tab, after about 1 minute, 5 minutes, 30 minutes, 2 hours, and 6 hours.
  • Events of one transaction_id arrive in order. A reversal is never sent before the credit it cancels, and a pending event that is still waiting for a retry is dropped once the final status is known.
  • Every attempt, with the status code your server returned, is listed in your dashboard under Logs, Postbacks.
  • The Send test postback button sends status 1 for test_user with a $1 payout, so you can check your handler before going live.

Example handler

$tx     = $_GET['tx'] ?? '';
$userId = $_GET['user'] ?? '';
$reward = $_GET['reward'] ?? '0';
$status = (int)($_GET['status'] ?? 0);
$expected = hash_hmac('sha256', $tx . ':' . $userId . ':' . $reward . ':' . $status, $postbackSecretKey);
if (!hash_equals($expected, $_GET['hash'] ?? '')) {
    http_response_code(403);
    exit;
}
$last = get_last_status($tx);
if ($status === 1 && $last !== 1 && $last !== 2) {
    add_to_balance($userId, $reward);
    save_status($tx, 1);
} elseif ($status === 2 && $last === 1) {
    subtract_from_balance($userId, $reward);
    save_status($tx, 2);
} elseif ($status === 3 && $last === null) {
    save_status($tx, 3);
} elseif ($status === 4 && ($last === null || $last === 3)) {
    save_status($tx, 4);
}
http_response_code(200);