Skip to main content

Server-to-Server Postbacks (v2)

Server-to-server postbacks deliver conversion events to your server using a GET request. By default, AdGem is set to send postbacks via GET request unless otherwise enabled by your Publisher Support Advocate.

Note

By default, AdGem only sends postbacks on Payable Conversion events. If you would like to enable Install postbacks to be sent to you, please reach out to your dedicated Publisher Support Advocate so we can enable this for you!

Postback Types

Reward / Conversion Postbacks

By default, AdGem sends a postback for a rewarded player conversion event. AdGem sends this postback to the Postback URL set in the AdGem dashboard.

Install Postbacks

For many of AdGem's offer campaigns we have the ability to send a postback to you when your players install an app from an offer. This allows you to better track the offer's performance with your players on your platform.

An install postback from AdGem follows the same Postback URL string schema as you have placed in the AdGem dashboard. For GET request postbacks, AdGem automatically appends &nonpayable_install_goal=true to the end of the string.

Setting Up Your Postback

Enabling Server Postback

From the AdGem Dashboard navigate to Properties & Apps and choose Edit from the Options drop down menu.

Edit button in dashboard

Once on the Properties & Apps menu, scroll down until you see Postback Options. Click on the radio input next to Server Postback.

Postback options in dashboard

Several new fields will appear that will allow you to configure your server postback.

Postback Key

The first field is your Postback Key. Copy this key to a secure place as you will need it to implement Postback Hashing. For security reasons, the Postback Key will only be visible once. See the Securing Your Postbacks section below to learn more about this important feature.

Postback keys in dashboard

Postback URL

Next, you will need to provide a Postback URL hosted on your server. AdGem will send a GET request to the URL you provide every time a conversion occurs from your traffic sources.

Postback URL input

The following macros are available for use in your postback URL. AdGem's server will replace these with actual values when sending the postback.

URL Encoding

All field values are URL encoded using PHP rawurlencode() RFC 3986 (which differs from JavaScript encoding). For example:

A {campaign_name} of Example App: Sports & Casino - CPE FTD (iOS, INCENT, Free, UK) will be returned as Example%20App%3A%20Sports%20%26%20Casino%20-%20CPE%20FTD%20%28iOS%2C%20INCENT%2C%20Free%2C%20UK%29

Best Practices
  • Include {payout} - Track the accurate decimal revenue earned for each conversion
  • Include {goal_id} - Track player progress on multi-reward offers (each goal has a unique ID)
  • Alphabetize parameters - Some frameworks sort query strings, so alphabetizing avoids authentication issues
MacroRecommendedDescription
{transaction_id}The unique AdGem ID of the offer conversion
{campaign_id}The unique ID of the campaign/offer that was completed
{payout}The decimal amount of revenue earned from the user completing the offer
{amount}The amount of virtual currency to reward the user (can be empty or null)
{goal_id}The unique ID of each goal in a campaign/offer that was completed
{player_id}The unique ID of the player/user on your system
{app_id}The unique ID for your app on AdGem
{offer_id}The unique offer version with which the user engaged (Offer API integrations only)
{offer_name}The name of the campaign/offer as shown in the Offerwall/API
{goal_name}The name of the completed goal
{store_id}The unique ID assigned by Google Play Store or Apple App Store
{tracking_type}The type of offer completed (e.g., CPI, CPE, CPA, CPC, Market Research)
{ad_type}The type of ad unit where the click originated (e.g., offerwall)
{all_goals_completed}Indicates if all goals have been completed for the campaign
{allow_multiple_conversions}Boolean indicating if the offer allows multiple conversions
{click_datetime}The date and time when the user clicked on the offer (EST)
{conversion_datetime}The date and time when the user completed the offer (EST)
{c1}Custom parameter value set by the publisher ^
{c2}Custom parameter value set by the publisher ^
{c3}Custom parameter value set by the publisher ^
{c4}Custom parameter value set by the publisher ^
{c5}Custom parameter value set by the publisher ^
{country}ISO country code for the user ^
{state}The user's state or region ^
{ip}The IP address of the user ^
{platform}The platform the device is using (e.g., ios) ^
{os_version}The user's Operating System version ^
{app_version}The version of your app where the click originated ^
{gaid}Google Advertising ID (when using Google Play Services) ^
{idfa}Apple Advertising ID (when user has not limited ad tracking) ^
{useragent}The User Agent from the user's default browser ^
Publisher-Provided Data

^ These macros contain data provided by the Publisher in the click_url. AdGem passes these values back in the postback.

Securing Your Postbacks

We provide two security measures for securing postback communication between AdGem and your server:

  1. IP Whitelisting - Verify the postback comes from AdGem's static IP
  2. Postback Hashing - Cryptographic verification of the postback integrity
Recommendation

We strongly recommend implementing both security measures to ensure postbacks are genuine. Without proper security, malicious actors could send fake postback requests to fraudulently earn virtual currency.

IP Whitelisting

Postbacks are sent from a static IP address. Whitelist this IP on your server. Contact support@adgem.com or your Publisher Support Advocate to get the production IP address.

Postback Hashing (v2 - GET Requests)

When you enable postback hashing by generating a Postback Key, you'll receive two new query parameters:

  • request_id - A unique UUID for each postback (no duplicates)
  • verifier - A cryptographic hash for validation

Verification Process:

  1. Remove the verifier parameter from the postback URL
  2. Hash the remaining URL using HMAC-SHA256 with your postback secret key
  3. Compare the calculated hash with the verifier parameter
hash_hmac('sha256', $postback_url, $postback_secret_key)

Full Example (PHP 7.1+):

// securely supply credentials using environment variables
define('ADGEM_WHITELIST_IP', $_ENV['ADGEM_WHITELIST_IP']);
define('ADGEM_POSTBACK_KEY', $_ENV['ADGEM_POSTBACK_KEY']);

// verify the static IP
if(ADGEM_WHITELIST_IP !== $_SERVER['REMOTE_ADDR']) {
http_response_code(403);
exit('Error: '.$_SERVER['REMOTE_ADDR'].' does not match the whitelisted IP address.');
}

// get the full request url
$protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http");
$request_url = "$protocol://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";

// parse the url and query string
$parsed_url = parse_url($request_url);
parse_str($parsed_url['query'], $query_string);

// get the verifier value
$verifier = $query_string['verifier'] ?? null;
if (is_null($verifier)) {
http_response_code(422);
exit("Error: missing verifier");
}

// rebuild url without the verifier
unset($query_string['verifier']);
$hashless_url = $protocol.'://'.$parsed_url['host'].$parsed_url['path'].'?'.http_build_query($query_string, "", "&", PHP_QUERY_RFC3986);

// calculate the hash and verify it matches
$calculated_hash = hash_hmac('sha256', $hashless_url, ADGEM_POSTBACK_KEY);
if ($calculated_hash !== $verifier) {
http_response_code(422);
exit('Error: invalid verifier');
}

// valid - safe to process the postback
http_response_code(200);
exit('OK');

Signature Verification (v3 - POST Requests)

For v3 POST postbacks, verify the request by hashing the request body with your secret key using HMAC-SHA256 and comparing it to the Signature header.

Route::post('/postbacks/adgem/v3', function () {
$receivedSignature = request()->headers->get('Signature', '');
$expectedSignature = hash_hmac('sha256', request()->getContent(), env('ADGEM_POSTBACK_KEY'));

if (hash_equals($expectedSignature, $receivedSignature)) {
return response()->noContent(200);
} else {
return response()->noContent(401);
}
});