FreeDon't want to build it yourself? We'll build your pre-landers.Have us build it for you →
Shared guide

Tracking and events

One link goes out from your page to ours. One postback comes back to your server when a lead sells. This page shows the shape of both, what goes in each field, how to pass the sale to Meta, and a short test to run before you spend.

The three sub IDs

Every partner uses the same meaning, so we can read your traffic the same way you do.

sub1
The source, or your sub-affiliate. A short fixed word per traffic type.
sub2
Your own click ID. It comes back to you on the sold-lead postback, so it must be unique per click.
sub3
The creative: the ad, the message or the drop that brought the click.
The three sub IDs for each traffic type
Trafficsub1sub2sub3
Meta and paid socialmetaYour click ID, mapped on your server to Meta's click dataThe ad ID
Text (SMS)smsYour click IDThe list or message code
EmailemailYour click IDThe creative or the drop
Path and surveypathYour click IDThe publisher or placement in your path
SearchsearchYour click IDThe ad or ad group
Native and displaynativeYour click IDThe ad or widget creative
Already use sub IDs differently? Some path and survey networks put the sending domain in sub1 or the publisher in sub3. Tell us your mapping before launch and we will read it your way. Never move your click ID out of sub2: it is the field that comes back on the postback.

The address fields you add

At the hand-off, add the confirmed property address to your tracking link. Our page opens with it already filled in. Try it here.

The address the visitor confirmed
Sub IDs

Your hand-off link

https://TRACKING-DOMAIN/AFFCODE/OFFERCODE/?sub1=meta&sub2=YOUR_CLICK_ID&sub3=AD_ID&street=123+Main+St&city=Denver&state=CO&zip=80202
Contact fields: accepted, strongly discouraged

If you already hold the visitor's first name, last name, phone or email, our page accepts them in the link as firstName, lastName, phone and email.

We strongly discourage it. Links end up in browser history, server logs and analytics on both sides, so those details would sit there too. Our page collects them anyway, under our own consent text.

Use a token, not personal details, in links. When you send a visitor to one of your own pages (a text or email link), put a random token in the link, for example ?t=k3J9xQ2mP7vL8wRt. Your server looks the token up and finds the address. Make it random, single-use where you can, at least 16 characters, and let it expire after a few days. Never build it from the data itself.

What each field means, and how to encode it
street
Street number and name, as the visitor confirmed it.
city
City.
state
Two letters, for example CO.
zip
Five digits.
lat, lng
Optional. The coordinates from your address lookup. They help us show the right house picture.

Encode the values properly (a space becomes + or %20). Building the link with the browser's own URL tools does this for you; the code is on the blueprint page.

Why your click IDs must survive

Your sub2 is the only thing that ties a sale back to a click.

When a lead sells, the postback carries your sub2 and nothing that identifies the visitor. If sub2 is empty, repeated, or lost on the way, the sale still happens but you cannot tell which ad earned it, and you cannot tell Meta.

Things that break it:

  • A redirect, link shortener or page builder that drops the query string.
  • Reusing one click ID for many visitors, or putting the campaign name in sub2.
  • Building the hand-off link from scratch and forgetting to copy the sub IDs across.
  • Not saving Meta's click data (the fbclid in the URL, and the _fbc and _fbp cookies) next to your click ID when the visitor first arrives.

The sold-lead postback

When a lead you sent sells, a conversion is recorded against your click. Your tracking account then calls the postback URL you set, server to server, with your sub2 and the payout.

  1. Add a postback URL

    In your tracking account, add a postback URL that points at your own server.

  2. Map the two macros

    Put the account's macro for sub2 in one parameter and its macro for the payout in another. Your account lists the exact macro names.

  3. Pass the sale on

    On your server, look up the click by sub2, ignore repeats, and pass the sale on to Meta.

https://YOUR-SERVER.example/postback?click_id=SUB2_MACRO&payout=PAYOUT_MACRO
When the sale arrives

The sale can arrive some time after the visit, not at the moment of the hand-off, because we verify the lead before it is offered to buyers. Not every lead sells, so not every click gets a postback. The receiver itself is part 7 on the blueprint page.

Feeding the sale to Meta

Send the sale to Meta from your server through the Conversions API, as its own event, matched to the click you logged.

TacticHow sureWhat we know
Optimize on the sold-lead signal, not Meta's lead countProven in our data one adOn one ad, the lead count Meta reported was far higher than the real leads our system recorded. Judge ads on sales.
Send raw-lead and sold-lead signals as separate eventsFrom our experienceKeep a hand-off and a sale as two different events so Meta never mixes them up. Your own handoff event is your raw-lead signal.
Deduplicate with an event IDUntestedGive each sold-lead event a stable ID built from your click ID. If a postback repeats, or you also fire the same event from the browser, Meta counts it once.
Pass the payout as the event valueUntestedLets Meta weigh sales by what they earned you. We have no result on whether it helps delivery.

An example sold-lead event, built from your click log. Fill the values from the click record, never from the visitor's contact details, which you do not have.

{
  "data": [{
    "event_name": "sold_lead",
    "event_time": 1792681512,
    "event_id": "c_8f2k1x-sold",
    "action_source": "website",
    "event_source_url": "https://yourbrand.com/check",
    "user_data": {
      "fbc": "VALUE_OF__fbc_COOKIE",
      "fbp": "VALUE_OF__fbp_COOKIE",
      "client_ip_address": "203.0.113.7",
      "client_user_agent": "Mozilla/5.0 (iPhone; ...)"
    },
    "custom_data": { "value": 0.00, "currency": "USD" }
  }]
}
The sending function, and testing it

Put the payout from the postback in value. While you test, use Meta's Test Events tool so test sales stay out of your real data.

JavaScript (Node)

// Node 18 or newer: built-in fetch, nothing to install.
var META_PIXEL_ID = process.env.META_PIXEL_ID;
var META_ACCESS_TOKEN = process.env.META_ACCESS_TOKEN;       // keep it on the server
var META_API_VERSION = process.env.META_API_VERSION;         // the Graph API version Meta lists as current
var META_TEST_EVENT_CODE = process.env.META_TEST_EVENT_CODE; // set while you test, from Test Events

// One sold-lead event, filled from the click you logged, never from contact details.
function sendServerEvent(eventName, click, value) {
  var payload = {
    data: [{
      event_name: eventName,
      event_time: Math.floor(Date.now() / 1000),
      event_id: click.click_id + '-sold',
      action_source: 'website',
      event_source_url: process.env.PAGE_URL,   // your pre-lander, for example https://yourbrand.com/check
      user_data: {
        fbc: click.fbc,
        fbp: click.fbp,
        client_ip_address: click.ip,
        client_user_agent: click.user_agent
      },
      custom_data: { value: value, currency: 'USD' }
    }]
  };
  if (META_TEST_EVENT_CODE) payload.test_event_code = META_TEST_EVENT_CODE;
  var url = 'https://graph.facebook.com/' + META_API_VERSION + '/' + META_PIXEL_ID +
    '/events?access_token=' + encodeURIComponent(META_ACCESS_TOKEN);
  return fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
  }).then(function (res) {
    if (!res.ok) throw new Error('Meta answered ' + res.status);
  });
}

Python

# Python 3 standard library only.
import json, os, time
from urllib.parse import urlencode
from urllib.request import Request, urlopen

META_PIXEL_ID = os.environ["META_PIXEL_ID"]
META_ACCESS_TOKEN = os.environ["META_ACCESS_TOKEN"]            # keep it on the server
META_API_VERSION = os.environ["META_API_VERSION"]              # the Graph API version Meta lists as current
META_TEST_EVENT_CODE = os.environ.get("META_TEST_EVENT_CODE")  # set while you test, from Test Events

# One sold-lead event, filled from the click you logged, never from contact details.
def send_server_event(event_name, click, value):
    payload = {"data": [{
        "event_name": event_name,
        "event_time": int(time.time()),
        "event_id": click["click_id"] + "-sold",
        "action_source": "website",
        "event_source_url": os.environ.get("PAGE_URL"),   # your pre-lander, for example https://yourbrand.com/check
        "user_data": {
            "fbc": click.get("fbc"),
            "fbp": click.get("fbp"),
            "client_ip_address": click.get("ip"),
            "client_user_agent": click.get("user_agent"),
        },
        "custom_data": {"value": value, "currency": "USD"},
    }]}
    if META_TEST_EVENT_CODE:
        payload["test_event_code"] = META_TEST_EVENT_CODE
    url = "https://graph.facebook.com/%s/%s/events?%s" % (
        META_API_VERSION, META_PIXEL_ID, urlencode({"access_token": META_ACCESS_TOKEN}))
    req = Request(url, data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"})
    with urlopen(req, timeout=10) as r:   # raises an error unless Meta answers with success
        r.read()

Plain steps

send_server_event(name, click, value):
  event = the example event, filled from the click record:
    event_name = name, event_time = now in seconds, event_id = click.click_id + "-sold"
    user_data = click.fbc, click.fbp, click.ip, click.user_agent
    custom_data = value and currency
  POST { "data": [event] } to the Meta Conversions API for your pixel, with your access token
  while you test, add your Test Events code so test sales stay out of your real data

Event names

Fire the first four from your page and log them on your server. The fifth comes back to you. The names are our recommendation Untested.

prelander_view
Your pre-lander loads. Fired by you.
qualified
The visitor passes your qualifying questions. Fired by you.
address_confirmed
The visitor taps "Yes, this is my home" and passes your bot filter. Fired by you.
handoff
Your page sends the visitor to ours. Fired by you. This is your raw-lead signal.
sold_lead
The lead sold. Arrives on your postback; your server sends it to Meta.

Fire your events on your own pages and from your own server only. Partner pixels and tags are not allowed on our pages.

Test it before you spend

Fire one test link, check the address arrives pre-filled, check your click ID comes back.

  1. Build one test link You

    Use a made-up click ID you will recognise, such as test-0001, and a covered address that is not a real seller's home. Your office works.

  2. Open it on a phone, on mobile data You

    Go through your own pre-lander to the confirm tap. Check your logs show one click and the four events, in order, all with test-0001.

  3. Check our page SellHouseFast

    Our page should open with street, city, state and ZIP already filled in, and the right house picture if you sent lat and lng. Stop there. Do not submit contact details for a test.

  4. Ask us for a test sale You

    Send us your test click ID. We can record a test conversion against it so you can watch the postback arrive.

  5. Check your click ID comes back You

    Your server should receive the postback with test-0001 and a payout field, send one sold-lead event to Meta's Test Events tool, and ignore the postback if it arrives twice.

https://TRACKING-DOMAIN/AFFCODE/OFFERCODE/?sub1=meta&sub2=test-0001&sub3=test&street=1+Example+Way&city=Denver&state=CO&zip=80202

Something missing? Send us the exact link you used and the time you opened it. Then see how to get set up.

Done for you

Have us build it for you

Tell us a little about you. We'll email you to take it from there. It's free.

What do you want built?

Prefer email?