---
title: "Postback parameters and partner integrations"
description: "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."
type: "reference"
audience: [network-manager]
keywords: [postback parameters, conversion action key, pixel key, sub1, sub10, sub-values, partner pass-through, traffic source click ID, clickid, custom params forwarding, AppsFlyer, MMP, attribution link, view-through, transaction ID, txid, payout, cp.op, max payout, att parameter, att placeholder, attribution type, default attribution type, system action, post-click, post-view, unattributed, attribution window, server-to-server]
ui_path: "Account → Advertisers → campaign → Conversion actions → Conversion tracking code → Postback (S2S)"
---

Fetch the complete documentation index at: https://epom.com/ad-server/docs/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Postback parameters and partner integrations

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](https://epom.com/ad-server/docs/guides/conversions/s2s-and-pixels.md#server-to-server-postback).

## 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](https://epom.com/ad-server/docs/guides/conversions/s2s-and-pixels.md#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](https://epom.com/ad-server/docs/reference/macros.md#custom-parameters)).

## 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](https://epom.com/ad-server/docs/guides/conversions/piggybacks.md)** 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](https://epom.com/ad-server/docs/guides/conversions/s2s-and-pixels.md#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}`](https://epom.com/ad-server/docs/reference/macros.md#a-misspelled-macro-or-a-clickid-placeholder-reaches-the-landing-page-as-typed)),
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.

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](https://epom.com/ad-server/docs/guides/conversions/track-third-party-ad-code.md#option-3--add-an-extra-third-party-impression-tracker-to-your-banner);
   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](https://epom.com/ad-server/docs/guides/conversions/s2s-and-pixels.md#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](https://epom.com/ad-server/docs/reference/macros.md#utility-and-advanced).

## 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](https://epom.com/ad-server/docs/guides/inventory/ad-tags-and-api.md)).

Do not confuse them with **Sub ID**, the legacy per-action click token in
[the token table](https://epom.com/ad-server/docs/guides/conversions/s2s-and-pixels.md#what-you-replace-and-what-is-replaced-for-you) —
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](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper.md) 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_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](https://epom.com/ad-server/docs/guides/conversions/conversion-actions.md#options-and-fields).

> **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

- [Get the pixel and S2S tracking code](https://epom.com/ad-server/docs/guides/conversions/s2s-and-pixels.md)
- [Why a conversion did not count](https://epom.com/ad-server/docs/guides/conversions/postback-troubleshooting.md)
- [Snippet tab options](https://epom.com/ad-server/docs/guides/conversions/s2s-snippet-options.md)
- [Create and edit conversion actions](https://epom.com/ad-server/docs/guides/conversions/conversion-actions.md)
- [Install the landing-page helper](https://epom.com/ad-server/docs/guides/conversions/landing-page-helper.md)
- [Piggyback pixels](https://epom.com/ad-server/docs/guides/conversions/piggybacks.md)
- [Macros reference](https://epom.com/ad-server/docs/reference/macros.md)
- [Run a report](https://epom.com/ad-server/docs/guides/analytics/run-report.md)