Daisy chaining and passbacks reference
Every control, grid column and fallback field of daisy chaining, when the default code is returned, how passbacks are counted and paid, the re-selection cap and the REST calls — the companion to the Set up daisy chaining and passbacks guide.
The companion to Set up daisy chaining and passbacks, which has the steps, the caveats and the fix-it checklist.
Daisy chaining controls
| Control | Range / values | What it does |
|---|---|---|
| Card switch | on / off | On creates the chaining record and auto-populates the chain in Banner mode. Off deletes the record, the order and every setting below, after a confirmation. |
| Lookup depth | 1–100, network default 5 | The walk gives up once this many excluded chain entries have been skipped, so it steps over at most depth − 1 of them and still serves the next one. Also raises the per-request reselection cap (see The reselection cap). |
| View 1-N per banner / per campaign | checkbox + 1–10 000 000, default 1 | Serve each entry this many times to the same visitor before moving down. Counted in a per-placement cookie. |
| Rotate 1-N times | checkbox + 1–10 000 000, default 1 | Caps how many times the exhausted chain restarts for one visitor. Disabled until view-per-entity is on. |
| Apply priorities | checkbox | Groups the chain by delivery priority and adds a per-tier enable + country list. |
| Per-tier checkbox + Country | one row per delivery priority; * = Global | Which delivery tiers take part, and in which countries. |
| Type | Banner / Campaign | Whether chain entries are banners or whole campaigns. Changing it rebuilds the chain. |
| Top / Up / Down / Bottom | — | Move the selected row. Enabled only with exactly one row ticked. |
| Refresh | — | Re-reads the chain from the server. |
The six delivery-priority tiers, strongest first: Premium, Standard High, Standard Medium, Standard Low, Non-guaranteed, House ads.
The network default for a new chain’s Lookup depth is set under Admin → Ad serving → Inventory & campaigns settings → Daisy chaining lookup depth (5 unless changed).
Chain grid columns
| Column | Notes |
|---|---|
| Priority | Display position, renumbered from 1 within each group when Apply priorities is on |
| Advertiser | Masked for users with the campaign-masking permission unless they own the advertiser |
| Campaign | As above |
| Banner | Only in Banner mode |
| Priority (group) | Only with Apply priorities on — shows the tier name plus the row’s country |
Fallback fields outside the card
| Field | Where | What it does |
|---|---|---|
| Default code | placement → Basic info → Advanced settings (Site, Mobile site and Non-standard site placements; needs Placement Default Code) | Markup returned when nothing filled. Macros are substituted with the same processor as banner markup: $$RANDOM$$, $$TIMESTAMP$$, $$PAGE_URL$$, $$COUNTRY$$, $$PLACEMENT_ID$$, $$CHANNEL$$, custom parameters and the rest of the request, page and placement set. Banner, campaign and advertiser macros and the click URLs come out empty; a recognised macro that cannot resolve is removed, an unrecognised token is left as written. |
Third-Party lookup limit (thirdPartyNetworkLookupLimit) | Deprecated. REST API only — the placement form no longer shows it | 0–10; a new placement starts at 5. Caps how many times one request may drop the chosen banner and re-select — the loop that recovers an impression a third-party tag passed back. It was the lookup budget of the server-side mediation that has since been removed; leave it alone. |
| Enable passback functionality | banner → Basic information | Lets this banner’s tag hand the impression back to Epom. Offered for a Code banner on a Standard / Non-standard / Mobile site placement, or a Template banner on a Standard / Non-standard site placement, to a role holding the Ajax invocation-code permission of that placement type, on a network with an AJAX invocation-code domain configured. |
| Banner passback code | banner → Basic information, after saving | The generated one-line <script> snippet to give the third party. It postMessages {by: <serving domain>, bId: <banner id>, pb: true} up the frame chain to the Universal JS loader, which re-requests the ad; it works with the Universal JS tag only. With more than one AJAX invocation-code domain, a Domain picker rebuilds the snippet for the chosen domain (the first is preselected); the choice is not saved with the banner. |
$$PASSBACK_INFO$$ / $$PASSBACK_INFO_ESC$$ | a fallback URL in tag or redirect flows that cannot post a message | Expands to `pb=customwl.unipass.p: |
When a request arrives carrying that passback value, Epom excludes the named banner from selection for this request, writes a passback log record for it, and re-runs banner selection.
A default code stored on a placement type the console does not draw the field on — the REST
API accepts defaultCode for a role holding Placement Default Code — is never returned for
an Application placement, never for a VAST/video request, and never from the native
JSON endpoint; a Video or Native placement only sees it on the request paths listed below.
When the default code is returned
The default code is not tied to any particular failure. Every reason a request ends without a banner funnels through the same “no banner” branch, so all of these reach it:
| Reason the request did not fill | Reaches the default code |
|---|---|
| No banner linked / campaign empty / no banner left in campaign | yes |
| Targeting or pre-targeting mismatch | yes |
| Frequency, click, action, no-click, IP or proxy capping | yes |
| Volume limits or per-action limits exhausted | yes |
| Size mismatch, placement type unsupported, in-stream type mismatch | yes |
| Page competitor / same-campaign exclusion | yes |
| Native creative missing required fields, or no native adapter | yes |
| DSP did not bid, timed out, or bid below the site CPM threshold | yes |
| DSP excluded: second-price only, empty DSP UID, traffic tier, minimum margin | yes |
| GDPR mediation restricted | yes |
Banner excluded by exclude parameter, or by an inbound passback | yes |
| Every chain entry over its view limit and rotations exhausted | no — the last chain entry is put back and serves |
| A banner was selected but produced empty markup | yes |
What decides whether you actually get it is how the request arrived, not why it failed. Each entry point has its own no-fill response, and only some of them carry the default code:
| How the request arrives | Returns the default code? |
|---|---|
| Universal JS / AJAX / the standard ad API — Standard, Non-standard, Mobile, Video, Email, Click-through, Native placements | yes |
| Iframe tag | yes |
| Universal JS / AJAX / the standard ad API — Application placement | no — empty response, no-impression logged |
| VAST / video request | no — an empty VAST document with an <Error> node |
| Native JSON endpoint | no — a native “no ads” JSON carrying a no-impression beacon |
| Video overlay request | no — empty response |
| Ad API v4 | no — JSON/XML/JSONP carrying only NO_IMPRESSION_BEACONS |
| Server-to-server call carrying a no-ads token | no — only the no-impression marker |
| Unknown or deleted placement key | no — empty response |
The site CPM threshold does not “trigger a passback”. It is a bid floor: DSP bids under it are rejected, and only if nothing else fills does the default code get its turn. See Set a site CPM threshold.
If Refresh after (sec) is set, the iframe tag’s no-fill response carries the same
<meta http-equiv="refresh"> as a filled one, so a placement that never fills keeps
re-requesting on that interval. The Universal JS / API no-fill response does not carry it.
How to confirm it fired
Two different mechanisms, two different metrics — and they live in different reports.
An unfilled request (default code returned, or nothing returned) is counted by the
No ads column, which comes from the no-impression event. That event is fired by a
no-impression.gif beacon embedded in the response, so it is only counted if the caller
actually loads the returned markup.
A passback is counted by five Advanced-report columns. The first four are gated by the Impressions report-column permission:
| Column | Means |
|---|---|
| Valid passbacks | A passback that matched an impression of the same banner in the same transaction, neither side flagged as fraud |
| Rejected passbacks | A passback with no matching impression, or with either side flagged as fraud |
| Fraud passbacks | The fraud subset of the row above — chiefly passbacks arriving from a blacklisted address |
| Rejected passbacks, % | The second row as a share |
| Unique passbacks | Distinct transactions with a valid passback; needs the Conversions and Unique report-column permissions instead |
Alongside them, Eligible impressions, Fill rate, % and Effective fill rate, % all account for passbacks.
Those four columns come from a merge that joins passback records against impressions. They exist only in the Advanced report. The General report has no passback column at all — there, a passback shows up as a missing impression, because each passback’s count, gross and net are subtracted from the matching impression figures.
Is a passback counted and paid?
A passback carries a price: the ad server prices the banner at CPM for the placement and country and stores gross and net on the passback record. What happens next depends on the report:
- General report — the passback’s impression, gross and net are subtracted from that banner’s impression aggregate. Net effect: the impression is undone and not paid.
- Advanced report — both events are kept, so you can see how many impressions were handed away and what they would have been worth.
If the passback record cannot be matched to an impression (they can be recorded by different ad servers), the subtraction is skipped and a negative row is written instead — but only when the network runs Advanced reporting, so that negative impressions never reach the General report.
Passbacks from blacklisted addresses are recorded as fraud and excluded from the impression subtraction, so the original impression stands.
The reselection cap
When the banner Epom picked cannot produce a usable response, the request drops it and
re-selects — at most max(thirdPartyNetworkLookupLimit, daisy-chaining Lookup depth) times.
Raising the chain’s depth silently raises that cap too, and passback logging is suppressed on
the retries so one passback is not counted twice. RTB (OpenRTB DSP) banners are never retried
this way, except on Native placements.
The placement’s own number is no longer on the form: read it with
GET /rest-api/placements/{placementId}.do and change it with the
thirdPartyNetworkLookupLimit parameter of POST /rest-api/placements/update/<type>.do (0–10;
a console save leaves it alone). The parameter is deprecated: a creative that cannot render is a
bad pick, and the reselection hides it. One such case is gone — on the native ad endpoint, a
Native placement with no native preferences of its own can build no response for any creative,
and selection used to hand it one creative after another until the budget ran out; it now
offers none, so the request answers no-fill once. The cases that remain (a template that
produces nothing, code that is empty after macro substitution, a missing file) are counted per
reason and named in the troubleshooting log, which is what has to reach zero before the budget
can go.
Over the API
POST /rest-api/placements/{placementId}/chaining.do switches daisy chaining on
(enabled=true, or a type: BANNER or CAMPAIGN) or off (enabled=false), and sets
lookupDepth, viewPerEntityEnabled / viewPerEntityCount, rotatingEnabled /
rotatingCount and groupByDeliveryPriority — only those you send. lookupDepth is 1–100,
as in the console, and the two counts 1–10000000; a value outside is a 400 that names the
range. enabled=false drops the chain, so sending it together with a type or an option is
refused (400) rather than answered as if the option had been set.
GET /rest-api/placements/{placementId}/chaining.do reads the chain together with those same
options and placementId / enabled (switched off, it answers
{"placementId":…,"enabled":false,"daisyChains":[]}; a missing or deleted placement is a
404), and POST /rest-api/placements/chaining/update-priority.do reorders it. Reading needs
the Daisy Chaining permission enabled or read-only; changes need it enabled, and update rights
on the site.
GET /rest-api/placements/{placementId}/daisy-chain.do answers the order the ad server
currently walks — as it holds it, so a change shows up after its next reload, within a few
minutes — with names only for advertisers you may view, and null while chaining is off. A
chain that is on but empty, without Apply priorities, comes back with an empty order: the
placement serves nothing. It needs the same permission and view rights on the site. See the
Placements API reference.
The per-tier country filters of a grouped chain are
POST /rest-api/placements/{placementId}/chaining/delivery-priority-filter.do with
deliveryPriority (PREMIUM, STANDARD_HIGH, STANDARD_MEDIUM, STANDARD_LOW,
NON_GUARANTEED or HOUSE_ADS), enabled and countries (ISO alpha-2 codes, repeat the
parameter; * or none sent means worldwide). enabled=true replaces the tier’s countries,
enabled=false leaves the tier out of the chain. The chain must already be grouped
(groupByDeliveryPriority=true), and an unknown country code refuses the call. Both the chain
read and the setup answer list the tiers kept in the chain under deliveryPriorityFilters,
each with its countries; right after grouping is switched on that is every tier with *.
Per-role notes
- Publisher — once granted Daisy Chaining, the chain is one of the few delivery controls that is genuinely theirs: they decide the order their demand is tried in.
- Network manager — the same card on the same placements, once granted.
- Read-only grant or Can View share — the chain and its settings are visible but cannot be changed; where chaining is off, the card does not appear. Changes need Can Edit or Is Owner.
Exact grants: see User roles.