Conversion tracking

How to fire a postback when a user converts on your site - required for CPA campaigns to attribute and bill correctly.

For CPA campaigns, we only know a user converted when you tell us. The mechanism is a postback: when a user does the thing you want them to do on your site, your server fires a quick request to ours, and we record the conversion against the original click. Without this wiring, your CPA campaign will serve clicks but never bill - because we have no way to know any of them turned into customers.

This page walks through the wiring. About 15 minutes of dev work, no SDK required.

info
You only need this for CPA campaigns. CPC campaigns bill on the click. CPM bills on the impression. Coregistration bills on the yes/no opt-in (handled inside the snippet - no postback needed).

The flow

  1. A user clicks your ad on a publisher's site.
  2. Our ad server adds ?efaid=<click_id> to your destination URL and redirects them there. The click ID is a 24-character hex string unique to this click.
  3. Your landing page captures the efaid value (URL param) and stores it - usually in a session cookie that survives across page loads.
  4. When the user completes whatever counts as a conversion (signup, purchase, install), your server fires a postback to https://ads.efind.com/serve/postback.php?efaid=<click_id>.
  5. We look up the click, record the conversion, and bill you at the bid rate that was in effect when the click happened.

Step 1: Capture efaid on your landing page

The URL in the user's browser when they land on your site will look like:

https://example.com/landing?efaid=ee40d35706798218cce42929

Grab the efaid parameter and store it. Cookies are the most common choice because they survive multi-step funnels. Example in JavaScript:

const url = new URL(window.location);
const efaid = url.searchParams.get('efaid');
if (efaid) {
  document.cookie = `efaid=${efaid}; path=/; max-age=${30*24*60*60}; SameSite=Lax`;
}

Or PHP, if you control the landing page server-side:

<?php
if (!empty($_GET['efaid']) && preg_match('/^[a-f0-9]{24}$/', $_GET['efaid'])) {
    setcookie('efaid', $_GET['efaid'], time() + 30*24*60*60, '/', '', true, true);
}
warning
The efaid survives only as long as your storage. A user who clears cookies between landing and converting will be unattributable. Most platforms see ~5-10% leakage from this - it's the cost of doing business across the open web.

Step 2: Fire the postback on conversion

The instant a conversion happens server-side, fire a single GET to our postback URL with the stored efaid:

PHP

$efaid = $_COOKIE['efaid'] ?? '';
if ($efaid !== '') {
    $url = 'https://ads.efind.com/serve/postback.php?efaid=' . urlencode($efaid);
    // Fire and forget - don't block the user's request on this.
    @file_get_contents($url, false, stream_context_create([
        'http' => ['timeout' => 2, 'ignore_errors' => true]
    ]));
}

Node.js

const efaid = req.cookies.efaid;
if (efaid) {
  fetch(`https://ads.efind.com/serve/postback.php?efaid=${encodeURIComponent(efaid)}`)
    .catch(() => {});  // fire-and-forget
}

Python

import requests
efaid = request.COOKIES.get('efaid')
if efaid:
    try:
        requests.get(f'https://ads.efind.com/serve/postback.php?efaid={efaid}', timeout=2)
    except requests.RequestException:
        pass

cURL (for testing or shell-based stacks)

curl -s "https://ads.efind.com/serve/postback.php?efaid=$EFAID"

Alternative: pixel-based (no server access)

If you can't fire a server-side postback - common on SaaS landing pages or simple thank-you pages - you can use a 1×1 image pixel instead. Drop this into the HTML of your conversion confirmation page (Shopify "thank you" page, Wix order confirmation, etc.):

<img src="https://ads.efind.com/serve/pixel.php?efaid=YOUR_EFAID_HERE"
     width="1" height="1" alt="" style="display:none">

You'll need to template the YOUR_EFAID_HERE with the stored value - most e-commerce platforms have a way to inject custom HTML with order metadata. Pixels are slightly less reliable than server postbacks (ad blockers, browsers blocking third-party requests) but they're easier to set up.

Response format

postback.php returns JSON. pixel.php returns the image but adds debug info in headers - useful for verifying the integration with your browser's dev tools.

JSON shape

{ "ok": true, "recorded": true,  "reason": null }            ← conversion logged ✓
{ "ok": true, "recorded": false, "reason": "already_recorded" } ← duplicate fire (no harm)
{ "ok": true, "recorded": false, "reason": "not_found" }       ← efaid doesn't match a click
{ "ok": true, "recorded": false, "reason": "bad_click_id" }    ← malformed efaid
{ "ok": true, "recorded": false, "reason": "window_expired" }  ← click was over 30 days old

HTTP status is always 200 regardless. The work-or-not signal is in recorded. Your integration should treat recorded: true as success and any other state as "fired but didn't take" - which is usually fine (most "no" reasons are operationally normal).

Testing your integration

  1. Set up a test campaign in the dashboard (any bid - you won't actually be charged for test conversions on draft campaigns).
  2. From a publisher's site or directly via our ad server URL, click on an ad and note the efaid appended to your landing URL.
  3. Trigger your conversion flow in a way that fires the postback.
  4. Check the campaign detail page in your dashboard - the Conversions stat should tick up within a minute.
  5. Fire the same postback again with the same efaid - the stat should NOT tick up a second time (idempotent).

Important behaviors

Next
Reading reports
arrow_forward