Browse the help center

Postback parameters and partner integrations

The parameters a server-to-server conversion postback carries — the conversion action key, partner sub-values sub1 to sub10, a traffic source's click ID, MMP links, payout and the att= attribution type — and how each is read.

Reference network manager Account → Advertisers → campaign → Conversion actions → Conversion tracking code → Postback (S2S)

What a server-to-server conversion postback can carry beyond the click ID, and how the ad server reads each piece: which key goes where, the partner’s own sub1–sub10 values, a traffic source’s click ID forwarded to its postback, mobile attribution links, payout and att=. The steps that produce the postback URL itself are on Get the pixel and S2S tracking code.

Who this is for

Network managers and integrators wiring a partner, an affiliate tracker or a mobile measurement partner to the Postback (S2S) tab. The tab needs Action Tracking: Server-to-Server on your role; see Per-role notes on the main page.

Two keys, and which one goes where

An action has two keys, and mixing them up is the most common way a postback integration silently fails. The window generates both correctly; you only need the distinction when you copy a key out of a grid by hand.

KeyLooks likeWhere it is usedDepends on the campaign?
Conversion action key52957d7778d48e0d9cd2b0f7e97bf4f3The Postback (S2S) URL’s action=, and the generated snippet’s config blockNo. The same key is valid from every campaign the action runs in
Pixel keyc6c206011ff5d5a019054bd73c41cf6b-22-4 — a hash, then the action id, then the campaign idThe key= on the pixel code blocks onlyYes. One per advertiser + campaign + action

The Conversion action key column on the campaign’s Conversion actions card and on the global Conversion Actions grid shows the first of the two, so a key copied from either grid works in a postback URL as is. Which campaign gets credited by a postback is decided by the click inside the token, not by the key.

The partner’s own breakdown: sub1 to sub10

Separate from the click id, and the thing partners ask for first. An affiliate appends their own values to the ad request — &sub1=affiliate42&sub2=creative-b — and expects them back on the conversion. Those ten names need no cp. prefix: they become custom parameters, and they ride inside the click ID — so they are still there when the postback arrives — whenever custom parameters are forwarded to conversions (see the recipe below). In creatives and landing URLs they read as $$SUB1$$ … $$SUB10$$ (use $$CUSTOM_PARAM_ESC(sub1)$$ when the value goes into a URL).

Only those ten are recognised bare — sub11 or a misspelling stays an ordinary request parameter and is not stored with the click, which is the usual reason a partner’s value goes missing between the click and the conversion. Anything else needs the cp. prefix (Macros reference).

Send conversions back to a traffic source’s postback with its click ID

A publisher or traffic source that buys on its own click ID ({clickid} in its system) wants each conversion reported to its postback URL with that ID. Epom carries the ID from the ad request to the conversion as a custom parameter:

  1. The traffic source appends its click ID to your tag, for example to a VAST tag URL: …&sub1={clickid} (any of sub1–sub10, or cp.<name> for another name).
  2. Turn on custom-parameter forwarding, or the value is not kept with the click. It is the network setting Pass conversion action custom params (ask Epom support or your account manager to change it): On for every campaign, or Per Campaign with Pass custom params in conversion action ticked on the campaign. Under Per Campaign a site or placement that has piggybacks forwards them as well.
  3. Add a URL (server-side) piggyback on that traffic source’s site or placement with its postback and the value: https://postback.example-traffic.com/conv?click_id=$$CUSTOM_PARAM_ESC(sub1)$$&event=$$ATTRIBUTION_TYPE$$. It fires when a conversion delivered through that site or placement is recorded. To report registrations only, pick that action in the piggyback’s Action field instead of All.
  4. The advertiser’s side is unchanged: the landing URL carries $$CLICK_ID$$ and the advertiser’s tracker calls the Conversion Tracking URL with it, as in Click ID.

A mobile measurement partner such as AppsFlyer gives you two links with its own placeholders — a click (attribution) link and an impression (view-through) link. Epom does not translate another platform’s placeholders (see misspelled macros and {placeholders}), so replace each one with the Epom macro that carries the value:

AppsFlyer parameterPut this Epom macro in it
pid (media source)The literal source name AppsFlyer gave you — not a macro
clickid$$CLICK_ID$$ — the value the install postback returns to Epom
c (campaign name)$$CAMPAIGN_NAME_ESC$$
af_c_id$$CAMPAIGN_ID$$
af_adset, af_adset_id$$PLACEMENT_NAME_ESC$$, $$PLACEMENT_ID$$ (or the zone macros)
af_ad, af_ad_id$$BANNER_NAME_ESC$$, $$BANNER_ID$$
af_siteid$$PUBLISHER_ID$$ (the site) or $$PLACEMENT_ID$$
advertising_id / idfa$$IFA$$ — filled only on in-app traffic that sends the device ID
af_ip, af_ua$$REMOTE_IP$$, $$CLIENT_USER_AGENT_ESC$$

The parameter names are AppsFlyer’s — check them against the link AppsFlyer generated for you.

  1. Click. Make the banner’s landing (click) URL the attribution link with the macros above: https://app.appsflyer.com/com.northwind.app?pid=northwind_media&c=$$CAMPAIGN_NAME_ESC$$&af_c_id=$$CAMPAIGN_ID$$&af_siteid=$$PLACEMENT_ID$$&clickid=$$CLICK_ID$$. Epom counts the click, then redirects to that link with the macros filled.
  2. View-through. Put AppsFlyer’s impression link, with the same macros, into the banner’s Third-Party tracking codes card as an image pixel — <img src="https://impression.appsflyer.com/com.northwind.app?pid=northwind_media&c=$$CAMPAIGN_NAME_ESC$$&af_siteid=$$PLACEMENT_ID$$" width="1" height="1" alt="">. The card’s code is macro-processed and loads with the ad (see Option 3 — add an extra third-party impression tracker; video banners use the VAST tracking card instead).
  3. Install postback back to Epom. In AppsFlyer’s partner postback settings, call the Conversion Tracking URL with the click ID AppsFlyer received: https://<ad-serving-domain>/at?action=<key>&clickid=<AppsFlyer's click-ID placeholder> — as in Click ID.
  4. Signed links. If the app requires signed attribution links, add $$APPS_FLYER_SIGNATURE(secret_key, expires_in_days, link_domain, link_path, pid, af_siteid, clickid)$$ at the end of the click link — see the Macros reference.

Can a conversion be posted with only a transaction ID?

No. $$TRANSACTION_ID_STR$$ identifies one impression in the logs; a conversion needs the value of $$CLICK_ID$$ (or the legacy Sub ID), which names the banner, placement and click. txid= is only another accepted name for the click-ID parameter, so a postback carrying a transaction ID in it is not matched.

To preset one on the placement rather than have the partner append it, add a Partner sub-value row on the placement’s Ad Tags & API → Settings tab; it then rides every tag the placement generates, VAST tags and the API request URL included (Ad tags and the serving API).

Do not confuse them with Sub ID, the legacy per-action click token in the token table — same word, unrelated meaning.

Price the conversion with payout

payout is the price of the conversion — what the advertiser is charged for it, in campaign currency — not what the order was worth. (The tab’s own one-line description of the parameter says What the conversion is worth, as a number; read that as the price of the conversion, not the order value.) The campaign’s Allow conversion action pricing overriding in URL checkbox is necessary and not sufficient. Two things have to be true: that checkbox is on, and the conversion is priced CPA — the payout replaces the campaign’s CPA price, so it only applies where the creative’s, failing that the campaign’s, pricing type is CPA. Where it is honoured, a Max payout per conversion on the campaign reduces a higher payout to that ceiling and the conversion is still counted. An older integration may be sending the same number as cp.op, the custom-parameter spelling, which still works — as does cp_op, for a stack that rewrites the dot, and the older cp.ap / cp_ap before them. A bare op= is not read at all. The endpoint deliberately refuses value, amount and sum: send one of those and it is ignored, the response carries XD-warning: payout-word-refused, and the conversion still counts at the campaign’s own rate. See Install the landing-page helper for the same distinction on the page side.

Declare the attribution type with att=

att= is not part of the generated URL — it is one of the two Optional parameters the tab lists beside it. Add it yourself when your tracker knows what it measured:

https://<ad-serving-domain>/at?subId=a1b2c3&att=click

The value states how you attributed the conversion. MMPs and affiliate trackers know whether their own attribution was click-based or view-based, and att= is where they say so.

Sending it is optional. Drop the parameter — or leave the placeholder in an older URL that was generated with one — and the conversion is still counted. What it is recorded as then falls to whatever the stored value carries — nothing, for a value expanded by the ordinary ad render — and from there to the action’s Default attribution type, which is Unattributed until someone sets it on the action (see When the partner cannot send att= below). Nothing is rejected for a missing or unusable att=.

You send (any of)Recorded as
click, postclick, clickthrough, ctPost-click
view, impression, imp, postview, viewthrough, postimpression, vt, vtaPost-view
nothing, or a placeholder left unreplacedWhat the stored value carries; failing that, the action’s Default attribution type

Parsing rules:

  • Values are case-insensitive, and - / _ separators are ignored: Post-Click, post_click and POSTCLICK all resolve to post-click.
  • Bare digits are rejected (att=1 means post-click in one vendor’s dictionary and post-view in another’s, so numeric codes are deliberately ambiguous and ignored).
  • An unrecognized value never rejects the conversion. It is counted, its attribution falls back exactly as if you had sent nothing, and the value you sent is noted in the troubleshooting log. Three values are read as “not sent” silently, without the unrecognized-value entry: an empty att=, the placeholder the window used to put in the URL ((click_or_view)), and its pre-149 spelling $$ATTRIBUTION$$. The last two are Epom’s own text, and counting them as partner garbage would fill the counter with it.
  • att= overrides what the stored value carries. A Sub ID the ad server expanded while processing a click already carries that click; att=view overrides it, because you measured the conversion and the ad server only knows where the value was expanded. It also wins over the action’s default, which is only read when nothing was declared at all.
  • System actions ignore att=. An action driven by a VAST or viewability tracking event is always recorded as System, whatever you send. Such an action offers neither Default attribution type nor Apply attribution window to server-to-server conversions on its edit page — nothing would ever read them — and on this tab the att line says so outright instead of describing what it would do.
  • A claimed click or view is what puts an S2S conversion under the attribution window. The window is opt-in per action (Apply attribution window to server-to-server conversions) and only applies to a conversion that claims one — because you sent att=, because the stored value carries a click, or because the action’s default says so. On an action with the option on, a claimed click is measured against the click window, and a postback arriving after that window closes is recorded as an expired-window diagnostic instead of a conversion. A conversion recorded as Unattributed has nothing to measure and is never window-checked.
  • att= only applies to server-to-server postbacks. On the cookie-pixel path it is ignored, because the ad server classifies those conversions itself from the stored click/view.

To verify: fire two test postbacks with att=click and att=view, then run a General report with the Post-click Conversions and Post-view Conversions columns enabled (they are opt-in — tick them in Reports Layout Settings), or check the Attribution Type column in the Conversions Log.

When the partner cannot send att=

Some integrations will never send it: an affiliate panel with no field for the parameter, a postback URL you do not control, a legacy tracker nobody will touch. Whoever manages the action can answer for them once, on the action — Account → Conversion Actions → action → Default attribution type classifies every conversion that arrived with nothing declared.

It is a fallback, not an override. Where a conversion’s attribution type comes from, most authoritative first:

  1. System, for a video/viewability tracking action — unconditional.
  2. att=, what the partner measured.
  3. What the ad server observed — a click it was processing when it expanded the stored value. Only the click redirect and the mail-ad click hand out a value marked that way; the ordinary ad render marks nothing, which is the common case.
  4. The action’s Default attribution type.
  5. Unattributed, which is what the default is until someone changes it.

So switching the default from Unattributed to Post-click cannot relabel a postback that sends att=view, and cannot touch a pixel conversion at all — the pixel path always classifies itself from the click/view cookie before the default is ever consulted. On a system tracking action the setting is not offered at all: step 1 is unconditional, so nothing below it is ever read.

Left at Unattributed it changes nothing about what you count: every conversion that arrives is recorded, exactly as before the setting existed. It only stops the reports from splitting them by attribution.

Setting a default has a second effect. An unattributed conversion is counted unconditionally because there is no event to measure against; a conversion claiming a click or a view has one. On an action that also has Apply attribution window to server-to-server conversions ticked, a postback arriving after that window closes then becomes a zero-revenue expired diagnostic instead of a counted conversion. Set the default to what the integration really does, and check the Expired post-click conversions / Expired post-view conversions columns after you change it.

Note: saving the default does not change ad serving at once. The ad servers work from a cache of the action settings that is reloaded on a schedule — every 10 minutes on a standard network — so postbacks arriving in the first minutes after the save are still classified by the previous value. Wait out a reload before deciding that a hand-fired test postback proves the setting does not work. The same delay applies to Apply attribution window to server-to-server conversions and to the two window values.

Corner cases and gotchas

Corner case: an S2S conversion is not checked against the action’s click/view window unless that action has Apply attribution window to server-to-server conversions ticked. The option is off on every action created before release 149, so existing integrations keep counting exactly what they counted before — but it is ticked on every action created since, so a new integration is window-checked from its first postback that claims a click or a view. The tracking pixel is window-checked either way. See Create and edit conversion actions.

Corner case: the two methods are symmetric here. The $$SUB_ID(...)$$ and $$CLICK_ID$$ / $$ACTION_DATA$$ macros both carry a click when the ad server expands them while processing one (a click-redirect URL, or the mail-ad click), and both carry nothing when the ordinary ad render expands them. The render is the common case, which is why att= exists: without it — and without a Default attribution type on the action — a working S2S integration reports every conversion as Unattributed.

Corner case: the action’s Default attribution type reaches server-to-server conversions only. Pixel conversions arrive already classified from the click/view cookie, and a system tracking action is always recorded as System, so neither ever falls through to the default.

Corner case: the window is measured from the moment the stored value was expanded, not from a click your partner recorded separately. On an action with the window option on, a value expanded at render and posted back with att=click is measured against the click window starting at that render. A postback replaying a value older than the window is recorded as an expired-window diagnostic instead of a conversion.

Corner case: a stored value expanded before release 149 carries no expansion time, so there is nothing to measure a window against. Postbacks replaying such a value are counted whatever the action’s default and window settings are — the window starts biting only as the older values age out of the partner’s database.

Corner case: a conversion on a campaign whose pricing type is not CPA is priced at zero, not at the campaign’s CPM or CPC rate. The conversion is counted and reported; it simply carries no revenue, because there is no CPA figure to read and the ad server does not substitute one. A payout in the URL does not rescue it either — the override only ever replaces a CPA price, so on a CPM- or CPC-priced campaign it is ignored even with Allow conversion action pricing overriding in URL on. If your conversions are arriving but every one is worth 0.00, check the campaign’s (or the creative’s) pricing type before you check the postback.

Corner case: a Conversion Tracking URL your partner copied from an earlier release carries an att= placeholder the current one leaves out. Nothing breaks: (click_or_view) and the older $$ATTRIBUTION$$ are both read as “not sent”, so the conversion is counted and classified exactly as if att= had never been there. Re-send the URL when convenient so the partner sees the shorter form.

Related pages

Please disable Adblock
Are you using adblock? Disable it and reload the page to proceed to the destination page.

A registration has already been submitted from this page. Please try again in 10 minutes.