EnglishTracker integrationHow tracking works

How tracking works

AlterCPA TOP is a postback source. When a lead changes state, we fire an HTTP request to the URL you configured for that event — straight into your tracker. We only send postbacks; we never receive them.

The {click} you embed in your deep link is the bridge: it travels from your ad → your tracker → AlterCPA TOP → back to your tracker when we fire the postback. Your tracker uses it to match our postback to its original click record.

The five events (= lead statuses)

EventFires whenConditional?
waitSubscriber pressed Start in the botNo — always fires
holdJoin request submitted (approval-required chats)No — always fires
approveSubscriber joined, or request was approvedNo — always fires
cancelSubscriber left voluntarilyOnly within the unsub window
trashSubscriber was kicked or bannedOnly within the unsub window

A leave after the unsub window still moves the lead to cancel / trash but fires no postback. The window is configured per campaign.

Macro reference

Macros are substituted into your postback URLs before we fire them — and they work just as well inside the message itself: the welcome text, screen text and broadcast body, plus every button URL (the CPA button, screen link buttons, the broadcast CTA). So you can drop a tracking link straight into the text — e.g. a Markdown link pointing at https://track.example/?click={click} — and {click} is filled in for each subscriber, exactly as it is in a postback. The same macro set applies everywhere.

MacroValue
{click}The click id from the deep link — the join to your tracker
{status}wait / hold / approve / cancel / trash
{campaign}Campaign code
{user}Your AlterCPA TOP user id
{lead}Internal lead id — useful as a dedup key
{subscriber}Subscriber’s Telegram user id
{username}Subscriber @username (may be empty if the user has none)
{chat}Tracked chat id
{time}UNIX timestamp of the event

Unknown macros are left untouched. Empty values become empty strings.

Global vs per-campaign templates

You have two places to set postback URLs:

  1. Profile (global) — five templates set once and reused everywhere.
  2. Campaign (per-campaign) — five templates for a specific campaign.

The rule is all-or-nothing per campaign:

  • Campaign sets any of its five templates → uses its own set as-is (empty fields in the campaign mean “no postback for that event”).
  • Campaign sets none → falls back to the global templates from your Profile.

This means if you override one postback on a campaign you have to set all five you care about on that campaign — there’s no per-event fall-through.

Delivery rules

  • GET only. We issue a plain HTTP GET. Every tracker we target (AlterCPA Lite, Keitaro, Binom, …) accepts GET postbacks.
  • Public hosts only. Your URLs are checked against an SSRF blocklist — loopback, private/RFC1918 ranges, link-local, and cloud-metadata IPs are all refused. Only public hostnames and IPs are accepted.
  • Retried with backoff. A failure is re-queued with a growing delay — 1 min, then 5 min (three delivery attempts in total). After the last one the event is logged as failed.
  • Success = HTTP 2xx (and a {"status":"ok"} body for AlterCPA Lite).
  • Idempotent per lead/event — we don’t re-fire an event for the same lead unless a new transition actually occurs.

The unsub window

The window is measured from the moment of joining. A leave always moves the lead to cancel or trash, but whether a postback fires depends on the window:

Leave relative to the windowLead statusPostback
Inside the windowcancel / trashfires
Outside the windowcancel / trashsuppressed

Window of 0 means “always fire” (no expiry). Typical values: 1 day, 7 days, 30 days. Set per campaign in the editor.

Next steps

Choose your tracker to get a ready-to-paste postback setup: