Piggybacks reference
Every type, column, macro and REST call of the Piggybacks card, how the ad server picks which rows fire on a conversion and how sampling is applied — the companion to the Piggyback pixels guide.
The companion to Piggyback pixels. The guide has the steps, the caveats and the fix-it checklist; this page holds every type, column, macro and REST call, plus how the ad server chooses which rows fire and how sampling is applied.
The two types
| Type | How it fires | Validated? |
|---|---|---|
| URL (server-side) | The ad server issues the request itself, asynchronously, straight after the conversion is recorded. Nothing runs in the visitor’s browser | Yes, where your network’s URL-validation switch is off — a malformed URL is refused on save and on inline edit (see The URL check below) |
| JS code (client-side) | The markup is returned with the conversion response and runs in the visitor’s browser, inside a frame of its own | No — any text is accepted, so a broken tag fails silently in the browser |
Neither type can affect the conversion itself: a URL piggyback’s response is logged and discarded, and a failure is not retried.
Which type to use
Use URL (server-side) whenever the partner accepts a plain call. It does not depend on the visitor’s browser at all, so it also covers app, CTV and server-to-server conversions, and it needs nothing installed on the conversion page.
Use JS code (client-side) when the partner will only credit a hit made by the visitor — Google Ads, GA4, Meta and most partner JS SDKs match on their own third-party cookie and on the visitor’s real browser. A server-side call from the ad server’s data centre is, to them, a request from the wrong device in the wrong country, and is either ignored or attributed wrongly.
Which snippets deliver a client-side piggyback
A JS row only runs if the conversion page uses an integration that can receive it, and only two of the five things Conversion Tracking Code hands out can:
| Integration | Delivers JS piggybacks? |
|---|---|
| Snippet tab — the landing-page helper | Yes, on both its post-click and post-view paths |
| Pixel Tracking tab → JS pixel tracking with piggybacks | Yes. This block only appears when you hold the piggyback permission and the campaign’s Piggybacks card is on |
| Pixel Tracking tab → JS pixel tracking | No — it fires a 1×1 image and cannot read a response |
| Pixel Tracking tab → Pixel tracking URL | No — an image cannot run anything |
| Postback (S2S) tab | No — there is no browser involved |
If a partner reports missing client-side hits, check which of these is actually on the conversion page first. URL rows are unaffected by all of this: the ad server calls them itself.
The two integrations treat consent differently. The Snippet withholds client-side piggybacks from a visitor who refused consent — the conversion is still counted, but no third-party tag is asked for. The older JS pixel tracking with piggybacks snippet has no such check and delivers them regardless. The consent it is reading is the one recorded on the publisher’s page at ad-request time, not a consent banner on the conversion page.
What you can paste into a JS row
Whatever the partner gave you. The row is classified once, on the way out, and always reaches the browser as working markup:
| What you paste | Read as | What runs |
|---|---|---|
A tag — <script>, <img>, <iframe>, a comment, several of them | Tag | Exactly as pasted |
A single URL with no spaces — https://partner.example.com/px?id=7 | Pixel URL | An <img> pointing at it |
A single URL whose path ends .js | Script URL | A <script src> pointing at it |
| Anything else — plain JavaScript with no tag around it | JavaScript | The same, wrapped in <script> |
&, ", < and > are escaped on the way into the tag, so a query parameter like
©=1 reaches the partner as a parameter and not as a ©. Several JS rows on one
conversion are delivered as one block, one row per line. The dialog’s Will run as block shows
the badge and the exact markup as you type.
The tag runs in a same-origin frame of its own, 1×1 and positioned off-screen, that stays on the
page after it has run. Inside it document.referrer is empty; top.location and the Referer
header on the tag’s own requests are intact; the advertiser’s page is parent.document; the
advertiser’s Content-Security-Policy applies to it.
Fire for
| Option | Conversions delivered |
|---|---|
| All conversions (default) | Every conversion that gets as far as firing piggybacks — post-click, post-view, System, and conversions nothing could classify (Unattributed) |
| Post-click only | Only conversions classified Post-click |
| Post-view only | Only conversions classified Post-view |
Narrowing means only what we positively identified, not everything we did not rule out: an Unattributed or System conversion reaches an All conversions row and no other. Use it when a partner is paid on one attribution model — a tracker that credits post-click should not receive post-view hits.
System conversions are the ones the ad server records itself for video and viewability events. That is what makes a pixel fired when a banner becomes viewable possible: pick the viewability action (AD VIEWABLE IMPRESSION-GLOBAL (id: N)) as the Conversion action, leave Fire for at All conversions, and use a URL (server-side) row — a JS row never runs on a viewability event. The full recipe is in Fire a third-party pixel when a banner becomes viewable.
Grid columns
| Campaign card | Site and placement cards |
|---|---|
| Type · Code/URL · Site · Placement · Conversion Action · Attribution · Sampling · Status | Type · Code/URL · Campaign · Conversion Action · Attribution · Sampling · Status |
The scoping columns tell you which card you are on, and they scope from opposite sides: a campaign narrows by Site and Placement, a site or a placement narrows by Campaign.
Attribution shows the row’s Fire for setting, Conversion Action shows
name (id: N) or All, and the Sampling column is absent altogether without the
sampling permission. A placement’s card is identical to a site’s, down to the columns and
the dialog — see the conversion tracking overview for a frame of it in place
alongside the other two placement-level tracking cards.
In-place editors cover Code/URL, Attribution, Sampling and Status on all three cards. Conversion Action, Site, Placement and Campaign are not editable after creation (over REST, the action of a site or placement piggyback can be changed in place). The Code/URL column shows every value in full: a long URL wraps, a long JS tag scrolls inside its own box.
How the ad server picks the rows
When a conversion for action A is recorded on campaign C through placement P on site S, the ad server gathers the piggybacks of C, S and P and keeps a row when:
- its scoping matches — a campaign row’s Site and Placement, a site or placement row’s Campaign, each ignored when left at “all”; and
- its Conversion action is A or is All — and All includes the system actions, so an All row also fires on every viewability and tracked video event; and
- its Fire for admits this conversion’s attribution type.
Rows that are switched off, and every row on a card that is switched off, are not loaded into the ad server at all, so they cannot match and cannot affect sampling. A conversion has to resolve to a campaign, a placement and an action before any piggyback is considered; expired and rejected conversions return before any piggyback code runs.
How sampling is applied
Sampling is a percentage from 0 to 100. A value outside that range is refused on every save — the dialog, the grid and the REST create and update — with an error on Sampling.
The two types are sampled differently, and the difference matters when you mix them.
JS code (client-side) rows each roll their own dice, after the Fire for filter. A 50% row fires on about half the conversions it is eligible for, independently of every other row.
URL (server-side) rows are decided once per conversion, from the lowest Sampling among the URL rows that fire for it — rows that matched the scoping and whose Fire for admits the conversion. Either every one of those URL rows fires or none does. A row that does not fire for a conversion has no say in its draw: a 5% Post-view only row leaves a 100% Post-click only row on the same campaign firing on every post-click conversion.
URL rows that fire for the same conversions share the lowest Sampling among them, so a 5% row throttles a 100% row next to it. If you need one partner sampled and another not on the same conversions, keep them on different levels of the hierarchy that do not overlap, or make the sampled one a JS row.
That same draw also changes how the conversion is recorded: a conversion whose URL piggybacks were sampled out is written to the advertiser’s statistics only, without the publisher-side attribution. Sampling is therefore not free — it costs publisher reporting on the conversions it drops. A conversion that no URL row fires for is never sampled out.
Sampling of 0 never fires; 100 always fires. Someone whose role cannot set sampling does not see the Sampling column, the REST API returns the field as null for them, and editing other fields keeps the stored sampling value.
Macros
Both types accept macros, but only the conversion-time subset. Piggyback macros in the
dialog footer is the reference list — 50 entries while the network’s entity-name macros are on
(the _NAME and _NAME_ESC entries drop out for a side that has them off), among them:
| Macro | Resolves to |
|---|---|
$$ATTRIBUTION_TYPE$$ | click, view, system — or empty for an Unattributed conversion |
$$TRANSACTION_ID_STR$$ / $$TRANSACTION_ID_LONG$$ | The ad request’s transaction id, base-36 or numeric |
$$CLICK_ID$$ | The click token, the same value as $$ACTION_DATA$$ |
$$GROSS$$ / $$NET$$ | The conversion’s CPA price |
$$CUSTOM_PARAM(<key>)$$ | A custom parameter carried through the conversion |
$$UUID$$, $$COUNTRY$$, $$GDPR$$, $$GDPR_CONSENT_IAB$$ | User, geo and consent values |
$$CAMPAIGN_ID$$, $$BANNER_ID$$, $$PLACEMENT_ID$$, $$ZONE_ID$$, $$PUBLISHER_ID$$ | Entity ids, with _NAME variants |
Request-derived macros — IP, user agent, referer, device, advertising id, incognito, time
zone — are absent, because a conversion carries no ad request to read them from. A few
deprecated aliases still work without being listed: $$TRANSACTION_ID$$ is an old spelling of
$$TRANSACTION_ID_STR$$, so an older row using it keeps working, but write new rows against
the list.
The macro list also documents expired_click and expired_view for
$$ATTRIBUTION_TYPE$$. Neither can ever reach a piggyback: expired conversions do not fire
one at all.
A macro the ad server recognises but cannot resolve is replaced with nothing; a macro it does not recognise is left as literal text. See the Macros reference.
The URL check
The check applies only to URL (server-side) rows and follows your network’s strict URL
validation, which is on unless Epom support has relaxed it for your network (a server-side
setting). With strict validation on: an http, https or ftp scheme (or a custom scheme
Epom has allowed for your network) and a host under a registered top-level domain. Macros survive
it — $$NAME$$, ${NAME}, {name}, {{name}}, [name], %%NAME%% and __NAME__ — but
reserved test domains do not: https://x.partner.example/px is refused as Invalid Url where
https://x.partner.example.com/px passes. With validation relaxed, the console dialog checks
nothing and saves any text, and over REST only the schemes that run script or read local files
(javascript:, data:, file: and the like) are refused. The campaign card’s in-place edit of
Code/URL additionally checks the address in the browser (URL must be valid) whichever way
validation is set. JS rows are never URL-checked.
Over the REST API
Site and placement piggybacks have the same life cycle over REST as in the grid:
POST /rest-api/piggyback/{site|placement}.do creates one, .../{id}/update.do changes
codeUrl, active, sampling (0–100), deliveryAttribution and the action it fires for
(actionId, or allActions=true), and .../{id}/delete.do removes it. A field you leave
out keeps its value; the type cannot be changed — delete the row and create it again. Every
call, including the /get.do list and the on/off switch, needs the piggyback pixel tracking
permission and at least a view share of the site (for a placement, of the placement’s site). Things the grid does not let happen are refused
rather than guessed at:
- an unknown
actionIdis an error onactionId. An action that is not in your action list — neither visible to you across the network nor shared with you — gets the same error, worded the same way, so the answer does not reveal whether the id exists. The console’s piggyback dialogs and grids apply the same rule; allActions=trueon create stores the row for every action of the campaign;- a site, placement or piggyback id that does not exist answers
404, and so does a deleted site or placement — a deleted site takes no new piggyback, and the list, update and delete of a deleted site’s or placement’s piggybacks answer404too; - a parameter the update does not take (such as
type) is listed back inignoredFields; POST /rest-api/piggyback/site.do(and.../placement.do,.../campaign/save.do) with anidedits that piggyback of the entity it names rather than creating a second one; anidthat belongs to another entity is refused, not moved.
Campaign piggybacks are the third level: POST /rest-api/piggyback/campaign/save.do, the batch
.../campaign/update.do and DELETE .../campaign/{id}.do need the piggyback pixel tracking
permission and update rights on the campaign, like the site and placement calls.
The /get.do list returns the rows even while the site’s or placement’s piggybacks are
switched off, as the grid does, with piggybacksEnabled telling whether they fire.
Permissions
| Who | What |
|---|---|
| Network manager | The whole feature on campaigns, sites and placements, plus the JS pixel tracking with piggybacks block on the Pixel Tracking tab |
| Publisher, advertiser | No Piggybacks card and no piggyback block, unless an administrator adds the feature to the role |
| Sampling | Hidden for every role until an administrator grants it separately |
| Campaign piggyback | Update on the campaign; view on the site it is scoped to |
| Site or placement piggyback | A view share of the site (for a placement, of the placement’s site); view on the campaign it is scoped to |
| Can view share | On a site or placement the card works in full (switch, Add, inline edits, Delete) while the rest of the page is read-only; on a campaign the grid renders without toolbar or checkboxes, cells and Status locked |
Exact grants: User roles.