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.
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.
| Key | Looks like | Where it is used | Depends on the campaign? |
|---|---|---|---|
| Conversion action key | 52957d7778d48e0d9cd2b0f7e97bf4f3 | The Postback (S2S) URL’s action=, and the generated snippet’s config block | No. The same key is valid from every campaign the action runs in |
| Pixel key | c6c206011ff5d5a019054bd73c41cf6b-22-4 — a hash, then the action id, then the campaign id | The key= on the pixel code blocks only | Yes. 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:
- The traffic source appends its click ID to your tag, for example to a VAST tag URL:
…&sub1={clickid}(any ofsub1–sub10, orcp.<name>for another name). - 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.
- 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. - 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.
AppsFlyer (and other mobile attribution links): click and view-through with Epom macros
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 parameter | Put 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.
- 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. - 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). - 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. - 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, ct | Post-click |
view, impression, imp, postview, viewthrough, postimpression, vt, vta | Post-view |
| nothing, or a placeholder left unreplaced | What 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_clickandPOSTCLICKall resolve to post-click. - Bare digits are rejected (
att=1means 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=viewoverrides 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 theattline 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:
- System, for a video/viewability tracking action — unconditional.
att=, what the partner measured.- 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.
- The action’s Default attribution type.
- 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 whyatt=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=clickis 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
payoutin 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 ifatt=had never been there. Re-send the URL when convenient so the partner sees the shorter form.