---
title: "Daisy chaining and passbacks reference"
description: "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."
type: "reference"
audience: [network-manager]
keywords: [daisy chaining reference, lookup depth, rotate, view per banner, apply priorities, delivery priority, default code, passback, passback columns, valid passbacks, rejected passbacks, no ads column, third-party lookup limit, chaining API]
ui_path: "Account → Publishers → site → zone → placement → Daisy chaining"
---

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.

# Daisy chaining and passbacks reference

The companion to [Set up daisy chaining and passbacks](https://epom.com/ad-server/docs/guides/delivery/daisy-chaining-passback.md), 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](#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 `postMessage`s `{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:<placement key>|txid:…|ch:…|b:<banner id>`, which is what the ad server parses on the way back in. |

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](https://epom.com/ad-server/docs/guides/delivery/cpm-threshold.md).

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](https://epom.com/ad-server/docs/api/placements.md).

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](https://epom.com/ad-server/docs/guides/admin/users-and-roles.md).

## Related

- [Set up daisy chaining and passbacks](https://epom.com/ad-server/docs/guides/delivery/daisy-chaining-passback.md)
- [Optimize ad rotation](https://epom.com/ad-server/docs/guides/delivery/optimization.md)
- [Set a site CPM threshold](https://epom.com/ad-server/docs/guides/delivery/cpm-threshold.md)
- [Create a placement](https://epom.com/ad-server/docs/guides/inventory/placements-create.md)
- [Macros reference](https://epom.com/ad-server/docs/reference/macros.md)
- [Report types](https://epom.com/ad-server/docs/guides/analytics/report-types.md)
- [Low fill rate](https://epom.com/ad-server/docs/troubleshooting/low-fill-rate.md)