Passback ad serving offers a way to manage traffic across all of your third-party networks from one console.

With passbacks, you can monetize more of your inventory by enabling a third-party network to "pass" any impressions it chooses not to fill "back" to your Kevel account, where you can route the impression to a different network for another chance at monetization.

The Kevel implementation of passbacks cuts down on some of the latency and lost impressions associated with a decentralized chain of default creatives pointing to different networks.

Passback Priority Behavior

Kevel only records and charges for an impression for the flight in the passback chain that actually served onto a page. In our example, if Tribal Fusion passed back the impression, but Pubmatic took it, Kevel would only record an impression for the Pubmatic flight.

If an impression makes it all the way through the passback bucket, and none of the networks choose to fill the impression, the impression will be passed back one more time and Kevel will look in your next lowest priority. In this example, let's suppose we have a flight of Google AdSense creatives set up at the House priority. This flight acts as a fall back and will fill all impressions that aren't picked up in the passback chain.

Setting Up AdChain

To create an adChain, you must set up the flights in the chain using an adChain-enabled priority. There are two types of adChain Priorities to choose from:

  • adChain Ordered: This serves ads in the chain in a waterfall pattern, passing back an impression from the highest rank flight to the next highest rank, etc. You can set the rank of the flights on the flight settings page for any of the flights in the chain.

  • adChain Optimized: While this also passes back impressions in a waterfall pattern, it will optimize the chain based on the flights' eCPM performance. The order of the chain will be set automatically.

Enable AdChain for a Priority


The default 'Networks' priority is already set to "AdChain Ordered", so you do not have to create a new one if you don't want.

  1. Go to Inventory tab --> Channels
  2. Click on the "Priorities" link to the far right of the Channel
  3. Click 'Add a Priority to this Channel' on top left
  1. Give it a Weight (which sets the waterfall order)
  2. Under Selection Method, choose adChain Ordered or adChain Optimized
  1. If you choose AdChain Optimized, you'll have an additional field around 'Remainder Percentage'. This is used to artificially bump up ads that have lower eCPMs. For instance, if you put 5, that means 5% of impressions won't be optimized for max revenue, but will instead be used to give ads that once performed poorly a 2nd chance to get data.


You are free to just put 0 as the Remainder Percentage. Some customers like to set this to 5%-10% to help remove flukes/noise, but it's up to you.



Once you set the selection method of a priority, you cannot set again. You will need to create a new priority to make changes.


AdChain and Second Pricing

If you are creating an adChain Optimized priority, you can check to "Enable Second Pricing", which will second price the impressions that are served in the chain. The Knowledge Base explains more about second pricing.


Overriding Timeouts

The default passback timeout (how long Kevel waits for a flight in an adChain to pass back before moving on to the next one) is 400ms. You can manually override the passback timeout for a priority using the Management API.

Set Up Flight Eligible for Passbacks

  1. Create a Flight
  2. Under Priority, select the "Networks (All Sites)" Priority, or another AdChain-set Priority
  1. Check the Enable Passbacks checkbox at bottom of the General Settings tab
  2. Save
  3. Repeat to add additional Flights to the chain

Additional Notes

  1. The 'Show Code' button provides the Passback code. Send it to your third-party network. Often times they will insert this code where they ask for a default creative. This code is not a traditional ad tag. It does not preview, for example. Rather, it is a call back to Kevel into your inventory to find a suitable alternate, based on the passback order and your other targeting rules.
  1. If you are setting up AdChain Optimized, you will also need to input an 'eCPM Override Value' in the 'eCPM Optimization' tab. More on that can be found here.

Viewing & Modifying AdChains

There are two ways:

By Inventory Tab

  1. Click on Campaigns --> AdChains. Here you will see a list of all AdChains. You can also filter adChain Priorities by Name, Channel, and the Selection Algorithm ("Ordered" or "Optimized")
  2. Click on the right Channel/Priority
  1. Here you will see the Flights in the AdChain.

AdChain Ordered will look like this, and you can reorder with the arrows on the right.


AdChain Optimized looks like this, and you can see the eCPM tied to each Flight.


Via Flights page:

  1. In General Settings, click on 'View AdChain' next to the 'Enable Passbacks' checkbox
  2. Click 'Confirm' on the next pop-up


Note that the adChain page displays both passback enabled and disabled flights. To enable or disable a flight for passbacks (i.e. make it active within the adChain), you will need to edit the flight on the flight settings page.

Enabling Multiple-Flight Passback Codes

To enable passback codes that are able to handle passing back from multiple flights, you will need to make some minor changes to the passback code.

In the passbackToKevel(); function, add brackets inside the function to create an array. Then add the flightIds of the flights you want to pass back, separated by commas.

The updated code looks like this:

<script type="text/javascript" src="https://static.adzerk.net/ados-passback.js"></script>
<script type="text/javascript">

Set Manual Timeouts

If your users are using slow internet connections, or if a third-party partner is taking longer than the default of 400ms to return an ad, the adChain may pass back an impression even though an ad is eligible to serve.

You can set a manual override of the timeout on the ad request level. Insert the function ados_setPassbackTimeout(); into your ad code with the timeout as the value. For example:

ados.run.push(function() { 
  ados_add_placement(4161, 260871, "azk2924", 5); 

You can also override the timeout on an adChain priority using the Management API.

View an Ad Candidate List

You can view an ad candidate list (a list of all eligible creatives for a given request) using a combination of adChain and a Decision API request. This will show you a list of the possible creatives that could serve given their targeting criteria, such as geo-targeting, custom targeting, etc.

Create Candidate List

  1. All the candidate creatives must be in flights in the same adChain-enabled priority.
  2. The priority must be the top priority in the channel. Otherwise, the request could contain ads from outside the candidate list.

To Retrieve Candidate List

  1. Make a Decision API request for a site in the channel that contains the candidate list.
  2. The response will make a decision in the div object. This is one of the eligible creatives.
  3. The remainder of the eligible creatives will be listed in the adChain object.

Ad Sequencing

You can use adChain to serve ads in a sequence (i.e. with each page refresh, the next ad in a chosen order will display.)

There are several limitations:

The adChain priority you use must have a higher priority setting than any other priorities serving ads to the eligible placements. Otherwise, an ad from the higher priority may interrupt the sequence.

You must serve the ads in an environment where frequency capping is available.

Creating a Sequence

  1. Create a flight in an adChain Ordered priority.
  2. Upload the first ad in the sequence with any applicable targeting.
  3. Enable frequency targeting to display the ad 1 time per 1 hour. (You may choose to extend the frequency capping period if you want the sequence to appear less frequently. At this time, the sequence cannot serve more often than once per hour.)
  4. Upload the next flight in the sequence with the same frequency targeting settings.
  5. Arrange the order of the flights by clicking 'View AdChain' in General Settings for a Flight in that Priority --> Confirm --> the arrows on the far right (you can also go to Campaigns --> AdChains)

Troubleshooting AdChain

In some cases, an adChain request may return a blank ad, which is sign that the third- party provider has failed to render an ad or signal to Kevel to load the next flight in the adChain.

There are two general causes for why third-parties fail to pass back:

1. The adChain code is unable to break out of an iframe due to the same origin policy.

A typical example is when a third-party provider renders the adChain code in an iframe. The adChain code needs to access ados.js (present in the Kevel code on the page) to continue the passback process, but the same-origin policy prevents the third-party iframe from getting access to the parent page.

This error will sometimes cause the text of the adChain code to be displayed in the placement instead of an ad or a blank.

2. The adChain code is expecting a different <div> id than the Kevel <div> id present on the page.

The adChain code needs access to the Kevel <div> while the impression is passed back and the ad is served. If the <div> id becomes obscured, changed, or if another Kevel <div> renders inside it (in the case of passing back to another Kevel account), then the adChain will break.

To resolve this issue, do not pass back to other Kevel accounts. Make sure that the <div> id is consistent during the time the adChain requests ads and renders them, e.g. don't let CSS interfere with it.

Using Dev Console

  1. Open the developer console in your browser. In Chrome, select the menu, then More Tools > JavaScript Console. In Firefox, select the menu, then Developer > Web Console.
  2. Reload the page that contains the adChain request.
  3. The console will return several messages that indicate the status of the adChain:

Loading flightId XXXX: adChain has made a request to the third-party and is waiting for a response.

Received passback for flight XXXX: The third-party has declined to serve an ad, either by returning the adChain code or through a timeout. The next ad in the chain will attempt to serve.

Writing pixel for flightId XXXX: The third-party has returned an ad and the impression counting pixel has been written to the page. The ad chain now stops.

If there are errors with adChain, an error message will be written:

Uncaught Security Error: This indicates the same-origin error above. Use the alternate passback method for the third-party that generated the error.

Uncaught Exception Error: This indicates that adChain is unable to access the Kevel div. See the explanation above.

Diagnostic Ad Code

The following code snippet (see below, between //BEGIN CODE SNIPPET and //END CODE SNIPPET) can be pasted into the <head></head> of a site running ados.js.

This snippet logs to the "Debug" tab of the Chrome console some useful information about the passback chain, as events are received. You'll find the div ID, flight ID, advertiser ID of the flight, and the time elapsed (in milliseconds) between the most recent passback event and the one before it.

For example:

divID | flightID | advertiserID: 8362.942ms


  <script type="text/javascript">
  //have to load this one differently so that you get it on the load of the JS.
  window.ados = {};
  window.ados.events = {}
  <script type="text/javascript" src="ados.js"></script>
  <!--This code snippet offers extra debugging options sent to the "Debug" tab of the Chrome console-->
  <script type="text/javascript">
    var ados = ados || {};
    ados.run = ados.run || [];
    var timers = {};
    var start = function(info) {
      var pix = JSON.parse(zerk_base64.decode(info.veriPixel.match(/\?e=([^&]+)&/)[1]));
      console.time(timers[""+info.flightId] = info.div+"|"+info.flightId+"|"+pix.av);
    var end = function(flightId) {
    ados.run.push(function() {
      ados.on("passbackExec", function(args) { start(args.data) });
      ados.on("passbackNext", function(args) { end(args.data.flightId) });
      ados.on("passbackTimeout", function(args) { end(args.data.flightId) });
      ados.on("ImpressionCounted", function(args) { end(args.data.shim.fl) });

  <script type="text/javascript">
    var ados = ados || {};
    ados.run = ados.run || [];
    ados.run.push(function() {
      ados_add_placement(yourNetworkID, yourSiteID, "yourDivID", [yourAdTypes]);

  <div id="yourDivID"></div>