Postback Documentation

Connect CPAlead to your server to record completed offers and reward your users.

Set up your postback with your own AI

Copy this prompt into your AI chat. It includes questions for your AI to ask you, a full setup guide, and steps to fix common postback problems.

The prompt does not include your saved URL or password. Use placeholders for passwords and other private details in your chat.

Read the full prompt

Quick start

A publisher postback is an HTTP GET request from CPAlead to your server after a qualifying conversion. Use it to credit cash or points, unlock content, or update your own reports.

  1. 1

    Identify your user

    Pass your user ID or a click reference in subid when the user opens an offer.

  2. 2

    Save your postback URL

    Enter your receiver URL in Configuration, turn on Postback Enabled, and click Save.

  3. 3

    Test your receiver

    Use a safe test user, check the response, and confirm that your system records the reward once.

  4. 4

    Check real deliveries

    Open Postback Logs to review delivery results for completed offers.

Tracking URL Setup

Pass your own user ID, wallet ID, transaction ID, or click reference into our tracking URL so the same value comes back to you later in the postback.

https://{tracking_domain}/view.php?id=1234&pub=1234&subid={Tracking_or_Subid}&subid2={Additional_Subid}&subid3={Additional_Subid}

Start with the real tracking link from your CPAlead account. Use a value that your server can match to a user. A static subid can identify a page or traffic source for reports, but it cannot tell your server which visitor to reward.

Mobile advertising IDs

For mobile app flows, you can also pass IDFA (Apple) or GAID (Google) alongside your subids

https://{tracking_domain}/view.php?id=1234&pub=1234&subid={Tracking_or_Subid}&subid2={Additional_Subid}&subid3={Additional_Subid}&idfa={Your_Users_IDFA}&gaid={Your_Users_GAID}

Postback URL Setup

Inside Postback Configuration, save the URL that should receive your conversion notifications. Start with the minimum macros you actually need, then add more fields only if your system uses them.

https://example.com/postback/cpalead.php?subid={subid}&lead_id={lead_id}&campaign_id={campaign_id}&campaign_name={campaign_name}&payout={payout}&password={password}

Replace the example domain and path with your receiver. Keep the placeholders in braces. CPAlead fills them in with URL-encoded conversion values.

The parameter names must match what your receiver expects. For example, user_id={subid} sends your user reference to a receiver that expects user_id.

Example with all supported fields

Add only the fields your receiver needs.

https://example.com/postback/cpalead.php?campaign_id={campaign_id}&campaign_name={campaign_name}&subid={subid}&subid2={subid2}&subid3={subid3}&idfa={idfa}&gaid={gaid}&payout={payout}&ip_address={ip_address}&gateway_id={gateway_id}&lead_id={lead_id}&country_iso={country_iso}&event_key={event_key}&event_name={event_name}&event_payout={event_payout}&password={password}&virtual_currency={virtual_currency}

Rewards and offer events

Use {payout} as your reward amount, or convert it to your own points. {virtual_currency} is typically 0 in the standard publisher flow.

Use lead_id to credit each reward once. Event callbacks and conversion or reversal records include event_key and event_name. A later event has a new lead_id even when it belongs to the same offer and user.

Macros and aliases

These are the primary CPAlead macros you can place in your URL. They will be replaced with live conversion values when your postback is sent. Use the lowercase names below. Unsupported macros are rejected when you save or test your URL.

{campaign_id}
CPAlead campaign or offer ID for the conversion.
{campaign_name}
CPAlead campaign or offer name.
{subid}
Your primary user ID or click/reference ID passed on the tracking URL. Use a static value only for page, site, or traffic source reporting.
{subid2}
Optional secondary tracking value passed on the tracking URL.
{subid3}
Optional tertiary tracking value passed on the tracking URL.
{idfa}
iOS advertising ID if you passed it on the tracking URL.
{gaid}
Android advertising ID if you passed it on the tracking URL.
{payout}
CPAlead payout amount for the conversion.
{ip_address}
Visitor IP captured on the converting click.
{gateway_id}
Locker or offer-wall tool ID when a CPAlead tool generated the conversion.
{lead_id}
Unique CPAlead lead/conversion ID. Best macro for deduping retries.
{country_iso}
Two-letter country code such as US.
{event_key}
Safe event key such as event_1 for event-based offers. Blank for non-event conversions.
{event_name}
Publisher-safe event name such as Event 1 or a manually reviewed event label. Blank for non-event conversions.
{event_payout}
Publisher payout amount for the completed event. For event-based offers this matches {payout}. Blank for non-event conversions.
{password}
Your saved postback password, if you include {password} in the URL.
{virtual_currency}
Virtual-currency amount from the event. In the standard publisher flow this is typically 0.
Supported compatibility aliases
{offer_id}
Alias of {campaign_id}.
{offer_name}
Alias of {campaign_name}.
{amount}
Alias of {payout}.
{transaction_id}
Alias of {lead_id}.
{ip}
Alias of {ip_address}.
{country_code}
Alias of {country_iso}.
{event_label}
Alias of {event_name}.
{event_amount}
Alias of {event_payout}.

Delivery and security

Use HTTPS and the final receiver URL. Your endpoint should accept GET requests without a browser login or challenge and return HTTP 2xx well within 15 seconds. Do not rely on redirects.

Set a long, random Postback Password in Configuration, include {password} in your URL, and check that secret on your receiver before processing a reward. Keep passwords out of logs and AI chats.

Store {lead_id} uniquely and handle the conversion record and reward together. If the same valid lead arrives again, return success without crediting it twice.

CPAlead relay IP

If your endpoint restricts inbound traffic, whitelist the CPAlead relay IP shown below so your server will accept postback delivery.

34.69.179.33

Test before using real rewards

The tester sends a real request to your receiver. Use a safe test user or a staging endpoint so sample requests cannot credit real balances. Check your own records as well as the HTTP response.

For troubleshooting steps, copy the AI prompt at the top of this page. You can also review real delivery attempts in Postback Logs.

PHP example

This example checks a shared password, prevents duplicate rewards, and records a conversion. Replace the database settings and adapt the user lookup and reward rule to your own system. The saved query data leaves out the password.

View the PHP receiver example
<?php
declare(strict_types=1);

/*
 * CPAlead publisher postback example.
 *
 * Recommended CPAlead URL:
 * https://example.com/postback/cpalead.php?subid={subid}&lead_id={lead_id}&campaign_id={campaign_id}&campaign_name={campaign_name}&payout={payout}&password={password}
 *
 * Notes:
 * - {subid} should usually be your internal user ID or click/reference ID when you need to reward one visitor.
 * - A static {subid} is okay for page, site, keyword, or traffic source reporting, but not for visitor-level rewards.
 * - {lead_id} should be stored uniquely so retries do not double-credit a user.
 * - {payout} is the most reliable reward input in the current publisher flow.
 * - {virtual_currency} is currently sent as 0 in the standard publisher postback flow.
 */

const DB_HOST = '127.0.0.1';
const DB_PORT = '3306';
const DB_NAME = 'your_database';
const DB_USER = 'your_user';
const DB_PASS = 'your_password';
const POSTBACK_PASSWORD = 'replace_with_your_secret';

$subid = trim((string) ($_GET['subid'] ?? ''));
$leadId = (int) ($_GET['lead_id'] ?? 0);
$campaignId = (int) ($_GET['campaign_id'] ?? 0);
$campaignName = trim((string) ($_GET['campaign_name'] ?? ''));
$payout = (float) ($_GET['payout'] ?? 0);
$countryIso = strtoupper(substr((string) ($_GET['country_iso'] ?? ''), 0, 2));
$ipAddress = trim((string) ($_GET['ip_address'] ?? ''));
$password = (string) ($_GET['password'] ?? '');

if (POSTBACK_PASSWORD !== '' && !hash_equals(POSTBACK_PASSWORD, $password)) {
    http_response_code(403);
    exit('Invalid postback password.');
}

if ($subid === '' || $leadId <= 0) {
    http_response_code(400);
    exit('Missing subid or lead_id.');
}

$dsn = sprintf('mysql:host=%s;port=%s;dbname=%s;charset=utf8mb4', DB_HOST, DB_PORT, DB_NAME);
$pdo = new PDO($dsn, DB_USER, DB_PASS, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

$pdo->beginTransaction();

try {
    /*
     * Create a table like:
     * CREATE TABLE cpalead_postbacks (
     *   id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
     *   lead_id BIGINT UNSIGNED NOT NULL UNIQUE,
     *   user_id VARCHAR(100) NOT NULL,
     *   campaign_id BIGINT UNSIGNED NOT NULL,
     *   campaign_name VARCHAR(255) NOT NULL,
     *   payout DECIMAL(10,4) NOT NULL DEFAULT 0,
     *   country_iso CHAR(2) NULL,
     *   ip_address VARCHAR(45) NULL,
     *   raw_query TEXT NULL,
     *   created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
     * );
     */
    $insert = $pdo->prepare(
        'INSERT INTO cpalead_postbacks
            (lead_id, user_id, campaign_id, campaign_name, payout, country_iso, ip_address, raw_query, created_at)
         VALUES
            (:lead_id, :user_id, :campaign_id, :campaign_name, :payout, :country_iso, :ip_address, :raw_query, NOW())'
    );
    $insert->execute([
        ':lead_id' => $leadId,
        ':user_id' => $subid,
        ':campaign_id' => $campaignId,
        ':campaign_name' => $campaignName,
        ':payout' => $payout,
        ':country_iso' => $countryIso ?: null,
        ':ip_address' => $ipAddress ?: null,
        ':raw_query' => json_encode(array_diff_key($_GET, ['password' => true]), JSON_UNESCAPED_SLASHES),
    ]);
} catch (PDOException $e) {
    // MySQL duplicate-key error: the lead was already processed.
    if (($e->errorInfo[1] ?? null) === 1062) {
        $pdo->rollBack();
        http_response_code(200);
        exit('Duplicate lead ignored.');
    }

    $pdo->rollBack();
    throw $e;
}

/*
 * Example reward logic:
 * - If you run a cash-back system, you may choose to credit $payout directly.
 * - If you run a points system, convert $payout to your own point value here.
 */
$rewardAmount = $payout;

$updateUser = $pdo->prepare('UPDATE users SET balance = balance + :amount WHERE id = :user_id');
$updateUser->execute([
    ':amount' => $rewardAmount,
    ':user_id' => $subid,
]);

if ($updateUser->rowCount() !== 1) {
    $pdo->rollBack();
    http_response_code(404);
    exit('User not found.');
}

$pdo->commit();

http_response_code(200);
echo sprintf(
    'OK lead_id=%d user=%s campaign_id=%d payout=%.4f',
    $leadId,
    $subid,
    $campaignId,
    $payout
);