Skip to content

Publisher Integration Guide ​

This guide covers how to integrate ads into your website. We offer two integration approaches: adflow.js Quick Integration (recommended, simplest) and S2S Configuration (manual setup, more flexible). For native iOS / Android apps, see Prebid Mobile SDK.

Integration Modes Comparison ​

Dimensionadflow.js Quick IntegrationS2S Configuration
ComplexityMinimal — just 2 lines of HTMLModerate — requires JS coding
Prebid.js KnowledgeNot requiredRequired
Ad FormatsBanner / Pop / Push / In-page Push / NativeBanner / Video / Native / Multi-Format
RenderingAutomatic iframe renderingFull control, custom rendering supported
ConfigurationHTML data attributesFull Prebid.js config API
Preferred DealSupportedSupported
Advanced FeaturesBasic configurationPrice Granularity, User Sync, First Party Data, etc.
Recommended ForQuick launch, simple pages, no frontend dev resourcesProduction environments, complex requirements, fine-grained control

adflow.js Quick Integration ​

adflow.js is a self-contained JavaScript SDK that encapsulates all Prebid.js S2S bidding logic into a single script. Publishers only need to include one <script> tag and place <iframe> tags at ad positions — no need to manually load Prebid.js or write any bidding code. It supports five ad types — Banner, Pop (popunder), Push (push notification), In-page Push (centered popup), and Native (inline card) — selected via the data-ad-type attribute on each ad slot.

Banner Debugger · Pop Debugger · Push Debugger · In-page Push Debugger · Native Debugger

Recommended

If you want the fastest and simplest integration, adflow.js is the best choice. No need to understand Prebid.js configuration details — just two lines of HTML to get started.

Usage ​

Integration requires just two steps:

1. Include the adflow.js Script

Add a <script> tag to your page with global configuration via data attributes:

html
<script src="https://assets.revosurge.com/js/adflow.min.js"
    data-account-id="your-account-id"></script>

2. Place Ad Slots

Add <iframe> tags where you want ads to appear:

html
<iframe data-adflow-ad
    data-placement-id="your-placement-id"
    width="300" height="250"></iframe>

adflow.js automatically handles the entire flow: Load Prebid.js → Configure S2S → Discover ad slots on the page → Run auction → Render winning ads.

Ad Types ​

adflow.js supports five ad types. The script integration is identical for all of them — the only difference is the data-ad-type attribute on the ad slot <iframe>.

Banner (default) ​

A standard in-page banner. The winning creative renders directly inside the <iframe>. When data-ad-type is omitted the slot is a Banner; set its size with width / height.

html
<iframe data-adflow-ad
    data-placement-id="your-placement-id"
    width="300" height="250"></iframe>

Pop (popunder) ​

Add data-ad-type="pop". A Pop ad has no in-page creative: after the auction, the winning ad opens in a background ("popunder") window on the user's first click anywhere on the page — browsers require window.open() to run inside a user gesture — and it fires only once per page. A Pop slot needs no width / height.

html
<iframe data-adflow-ad
    data-placement-id="your-placement-id"
    data-ad-type="pop"></iframe>

Pop Debugger

Push (push notification) ​

Add data-ad-type="push". A Push ad also has no in-page creative; instead it surfaces as a real browser notification via the Notification API (title, body, and icon come from the creative, and clicking it opens the landing page). It requires the user to grant notification permission (browsers only prompt from a user gesture in a secure HTTPS context). A Push slot needs no width / height — the notification icon is always requested at 192×192.

html
<iframe data-adflow-ad
    data-placement-id="your-placement-id"
    data-ad-type="push"></iframe>

Push Debugger

Disable the notification

Push ads pop a browser notification by default. To receive the bid without showing a notification, add data-push-notification="false" to the <script> tag.

In-page Push (centered popup) ​

Add data-ad-type="inpage-push". The auction request is identical to Push (a native creative with image + title + body), but it shows no system notification and needs no permission: on a win, adflow.js renders a popup card centered on the page — image full-width on top, title and body below, with a close button in the corner. Clicking the card opens the landing page. width / height set the requested image size, defaulting to 492×328.

html
<iframe data-adflow-ad
    data-placement-id="your-placement-id"
    data-ad-type="inpage-push"
    width="492" height="328"></iframe>

In-page Push Debugger

Native (inline card) ​

Add data-ad-type="native". The auction request is the same native creative as Push (image + title + body), but the ad renders inline at the slot's position instead of popping up: on a win, adflow.js inserts a card — image on top, title and body below, no close button — right where the <iframe> slot sits in your page flow, blending with the surrounding content. Clicking the card opens the landing page. No notification permission is needed. width / height set the requested image size, defaulting to 492×328.

html
<iframe data-adflow-ad
    data-placement-id="your-placement-id"
    data-ad-type="native"
    width="492" height="328"></iframe>

Native Debugger

Script Attributes ​

The following data attributes are supported on the <script> tag:

AttributeRequiredDefaultDescription
data-server-urlOptionalhttps://prebid-server.revosurge.comPrebid Server URL
data-account-idRequired—Account ID
data-bidderOptionalrevosurgeBidder name
data-timeoutOptional3000S2S timeout in milliseconds
data-prebid-urlOptionaljsdelivr CDNCustom Prebid.js URL
data-debugOptional—Enable debug mode (presence attribute, no value needed)
data-push-notificationOptionaltrueWhether a Push ad pops a browser notification when it wins; set to false to disable

Ad Slot Attributes ​

The following data attributes are supported on the <iframe> tag:

AttributeRequiredDefaultDescription
data-adflow-adRequired—Marks this element as an ad slot (no value needed)
data-placement-idRequired—Ad placement ID, obtained from the AdFlow dashboard
data-ad-typeOptionalbannerAd type: banner (default), pop (popunder), push (push notification), inpage-push (centered popup), or native (inline card)
widthOptional300 / 492Ad slot width in pixels. Creative size for banner (default 300); requested image width for inpage-push / native (default 492)
heightOptional250 / 328Ad slot height in pixels. Creative size for banner (default 250); requested image height for inpage-push / native (default 328)
data-adflow-responsiveOptional—Set to 1 so the iframe stretches to width: 100% of your parent container. You must provide and style that wrapper (see Responsive layout)
data-deal-idOptional—Preferred Deal ID

Responsive layout ​

data-adflow-responsive="1" only changes how the <iframe> is sized in the page: it fills the width of its parent element. AdFlow does not create an outer layout box for you. You are responsible for wrapping the iframe in a container (for example a <div>) and applying CSS for width, height, max-width, centering, and breakpoints so the slot matches your design.

Keep the width and height attributes on the iframe so the SDK can request the correct creative size; the wrapper controls how much horizontal space the slot occupies on screen.

Fixed-size slot (e.g. 728×90)

html
<style>
.your-ad-slot {
    width: 728px;
    height: 90px;
}
</style>
<div class="your-ad-slot">
    <iframe data-adflow-ad
        data-placement-id="your-placement-id"
        data-adflow-responsive="1"
        width="728"
        height="90"></iframe>
</div>

Fluid width with a max width

html
<style>
.your-ad-slot {
    width: 100%;
    max-width: 728px;
    height: 90px;
    margin: 0 auto;
}
</style>
<div class="your-ad-slot">
    <iframe data-adflow-ad
        data-placement-id="your-placement-id"
        data-adflow-responsive="1"
        width="728"
        height="90"></iframe>
</div>

Full Example ​

TIP

Below is a minimal complete page example that you can copy and use directly.

html
<!DOCTYPE html>
<html>
<head>
    <title>My Website</title>
    <script src="https://assets.revosurge.com/js/adflow.min.js"
        data-account-id="your-account-id"></script>
</head>
<body>
    <h1>My Website</h1>

    <!-- 300x250 Ad Slot -->
    <iframe data-adflow-ad
        data-placement-id="placement-1"
        width="300" height="250"></iframe>

    <!-- 728x90 Ad Slot -->
    <iframe data-adflow-ad
        data-placement-id="placement-2"
        width="728" height="90"></iframe>

    <!-- Ad Slot with Preferred Deal -->
    <iframe data-adflow-ad
        data-placement-id="placement-3"
        data-deal-id="deal-001"
        width="970" height="250"></iframe>

    <!-- Pop ad (no width/height needed) -->
    <iframe data-adflow-ad
        data-placement-id="placement-4"
        data-ad-type="pop"></iframe>

    <!-- Push notification ad (no width/height needed) -->
    <iframe data-adflow-ad
        data-placement-id="placement-5"
        data-ad-type="push"></iframe>

    <!-- In-page Push ad (width/height optional, default 492×328) -->
    <iframe data-adflow-ad
        data-placement-id="placement-6"
        data-ad-type="inpage-push"></iframe>

    <!-- Native ad (card renders inline at this position; width/height optional, default 492×328) -->
    <iframe data-adflow-ad
        data-placement-id="placement-7"
        data-ad-type="native"></iframe>
</body>
</html>

Debugging

Add the data-debug attribute to the <script> tag to view detailed [AdFlow] prefixed logs in the browser console. You can also inspect runtime configuration and slot info via the window.__adflow object.

Diagnostic Script ​

If you encounter issues with adflow.js, run the following diagnostic script in your browser's developer console (DevTools → Console) to quickly identify common problems:

js
fetch('https://assets.revosurge.com/js/adflow.diagnostic.js').then(r=>r.text()).then(eval)

The diagnostic script will check your integration setup and output actionable suggestions directly in the console.

adflow-tma.js (Telegram Mini App) ​

adflow-tma.js is a purpose-built ad SDK for Telegram Mini Apps (TMA). It uses the same Prebid.js S2S bidding engine as adflow.js, adapted for the Telegram WebApp environment: platform and device signals are read from Telegram.WebApp, cookie syncing is disabled (not applicable inside TMA), and ad link clicks are intercepted and forwarded through Telegram.WebApp.openLink().

Zero dependencies

adflow-tma.js automatically loads telegram-web-app.js if it is not already present — no separate script tag required.

Usage ​

Integration requires two steps:

1. Include adflow-tma.js

Add the script tag with your account ID:

html
<script src="https://assets.revosurge.com/js/adflow-tma.min.js"
    data-account-id="your-account-id"></script>

2. Place Ad Slots

Add <iframe> tags where you want ads to appear:

html
<iframe data-adflow-ad
    data-placement-id="your-placement-id"
    width="320" height="50"></iframe>

Script Attributes ​

The following data attributes are supported on the <script> tag:

AttributeRequiredDefaultDescription
data-account-idRequired—Account ID
data-server-urlOptionalhttps://prebid-server.revosurge.comPrebid Server URL
data-bidderOptionalrevosurgeBidder name
data-timeoutOptional3000S2S timeout in milliseconds
data-prebid-urlOptionaljsdelivr CDNCustom Prebid.js URL
data-debugOptional—Enable debug mode (presence attribute, no value needed)

Ad Slot Attributes ​

The following data attributes are supported on the <iframe> tag:

AttributeRequiredDefaultDescription
data-adflow-adRequired—Marks this element as an ad slot (no value needed)
data-placement-idRequired—Ad placement ID, obtained from the AdFlow dashboard
data-floorOptional—Per-slot bid floor. Valid numeric values are sent as ortb2Imp.bidfloor.
data-deal-idOptional—Preferred Deal ID
widthOptional320Ad slot width in pixels
heightOptional50Ad slot height in pixels
data-adflow-responsiveOptional—Set to 1 so the iframe stretches to width: 100% of your parent container. You must provide and style that wrapper (see Responsive layout)

Responsive layout ​

Same behavior as adflow.js: with data-adflow-responsive="1", the iframe uses the full width of the element you wrap it in. Provide your own container and CSS; keep width and height on the iframe for the requested slot size.

html
<style>
.your-ad-slot {
    width: 320px;
    height: 50px;
}
</style>
<div class="your-ad-slot">
    <iframe data-adflow-ad
        data-placement-id="your-placement-id"
        data-adflow-responsive="1"
        width="320"
        height="50"></iframe>
</div>

Full Example ​

TIP

Below is a minimal complete Telegram Mini App page that you can copy and use directly.

html
<!DOCTYPE html>
<html>
<head>
    <title>My Telegram Mini App</title>
    <script src="https://assets.revosurge.com/js/adflow-tma.min.js"
        data-account-id="your-account-id"></script>
</head>
<body>
    <h1>My Mini App</h1>

    <!-- 320x50 Banner Ad -->
    <iframe data-adflow-ad
        data-placement-id="placement-1"
        width="320" height="50"></iframe>

    <!-- 300x250 Rectangle Ad -->
    <iframe data-adflow-ad
        data-placement-id="placement-2"
        width="300" height="250"></iframe>
</body>
</html>

Debugging

Add the data-debug attribute to the <script> tag to view detailed [AdFlow TMA] prefixed logs in the browser console. You can also inspect runtime configuration and discovered slots via window.__adflowTMA.

Usage with tma.js SDK / @telegram-apps Packages ​

For Mini Apps built with the tma.js ecosystem (@telegram-apps/sdk, @telegram-apps/sdk-react, or @telegram-apps/sdk-vue), use adflow-tma-modern.js — a UMD/ESM module that accepts tma.js SDK values directly via a programmatic API. No script-tag injection or DOM-scanning workarounds needed.

Download

adflow-tma-modern.js is a separate file from adflow-tma.js. Reference it from https://assets.revosurge.com/js/adflow-tma-modern.js or import it as a local module in your bundler project.

Call AdFlow.init() once (typically at app root), then call AdFlow.requestAds() after your component mounts with a list of slot descriptors.

init() Parameters ​

ParameterRequiredTypeDescription
accountIdRequiredstringYour AdFlow account ID
serverUrlOptionalstringPrebid Server URL (default: https://prebid-server.revosurge.com)
platformOptionalstringFrom miniApp.platform(). e.g. 'ios', 'android'
userOptionalobjectFrom initData.user(). Used for targeting and frequency capping.
onReadyOptionalfunctionCalled once after init. Pass () => miniApp.ready().
openLinkOptionalfunctionCalled when a user clicks an ad link. Pass openLink from the SDK.
timeoutOptionalnumberAuction timeout in ms (default: 3000)
debugOptionalbooleanEnable verbose [AdFlow] logs in the console

requestAds() Slot Descriptor ​

FieldRequiredTypeDescription
elRequiredHTMLElementContainer element. AdFlow creates and injects an <iframe> into it.
placementIdRequiredstringAd placement ID from the AdFlow dashboard
widthOptionalnumberAd width in pixels (default: 320)
heightOptionalnumberAd height in pixels (default: 50)
floorOptionalnumberBid floor in USD, sent as ortb2Imp.bidfloor
dealIdOptionalstringPreferred Deal ID

React ​

js
import { useEffect, useRef } from 'react';
import AdFlow from 'adflow-tma-modern.js';
import { miniApp, initData, openLink } from '@telegram-apps/sdk-react';

function AdBanner({ placementId, width = 320, height = 50 }) {
  const containerRef = useRef(null);

  useEffect(function() {
    AdFlow.init({
      accountId: 'your-account-id',
      platform:  miniApp.platform(),
      user:      initData.user(),
      onReady:   () => miniApp.ready(),
      openLink:  openLink,
    });

    AdFlow.requestAds([{
      el:          containerRef.current,
      placementId: placementId,
      width:       width,
      height:      height,
    }]);
  }, []);

  return <div ref={containerRef} style={{ width, height }} />;
}

INFO

AdFlow.init() is idempotent — safe to call multiple times. Prefer calling it once at your app root so individual AdBanner components only need requestAds().

Vue 3 ​

vue
<template>
  <div ref="adContainer" :style="{ width: width + 'px', height: height + 'px' }" />
</template>

<script setup>
import { ref, onMounted } from 'vue';
import AdFlow from 'adflow-tma-modern.js';
import { miniApp, initData, openLink } from '@telegram-apps/sdk-vue';

const props = defineProps({ placementId: String, width: { default: 320 }, height: { default: 50 } });
const adContainer = ref(null);

onMounted(function() {
  AdFlow.init({
    accountId: 'your-account-id',
    platform:  miniApp.platform(),
    user:      initData.user(),
    onReady:   () => miniApp.ready(),
    openLink:  openLink,
  });

  AdFlow.requestAds([{
    el:          adContainer.value,
    placementId: props.placementId,
    width:       props.width,
    height:      props.height,
  }]);
});
</script>

S2S Configuration ​

For more granular control, or if you need to customize Prebid.js configuration (ad formats, first-party data, user sync, etc.), use the manual S2S configuration approach. This requires including Prebid.js on your page and writing bidding code.

S2S Debugger

Quick Start ​

Prerequisites ​

  • An AdFlow account with bid parameters obtained
  • A deployed website with pages available for ad placement

Step-by-Step Integration ​

1. Include Prebid.js

Load Prebid.js in your page's <head>. It's recommended to use a custom build (containing only the Bidder Adapters you need) to reduce file size.

html
<!-- Option 1: CDN (for testing only, includes all adapters) -->
<script async src="https://cdn.jsdelivr.net/npm/prebid.js@latest/dist/not-for-prod/prebid.js"></script>

<!-- Option 2: Custom build (recommended for production) -->
<!-- Download from https://docs.prebid.org/download.html -->
<script async src="/js/prebid.js"></script>

2. Define Ad Slots

Place ad containers on your page. The id must match the code in the Prebid.js configuration.

html
<!-- Ad containers -->
<div id="ad-slot-1"></div>
<div id="ad-slot-2"></div>

3. Configure Prebid.js

Initialize Prebid.js and add Ad Units. Replace placementId with the value obtained from your AdFlow dashboard.

js
var pbjs = pbjs || {};
pbjs.que = pbjs.que || [];

pbjs.que.push(function() {
    var adUnits = [{
        code: 'ad-slot-1',          // Matches the div id on the page
        mediaTypes: {
            banner: {
                sizes: [[300, 250], [336, 280]]
            }
        },
        bids: [{
            bidder: 'revosurge',
            params: {
                placementId: 'your-placement-id'  // Replace with your Placement ID
            }
        }]
    }];

    pbjs.addAdUnits(adUnits);

    pbjs.requestBids({
        timeout: 2000,
        bidsBackHandler: function() {
            // Bidding complete, send to ad server
            sendAdServerRequest();
        }
    });
});

4. Render Ads

After bidding completes, render the winning ad onto the page.

js
function sendAdServerRequest() {
    // If using GAM, refer to the "Ad Server Integration" section
    // Below is a simple rendering approach without an ad server
    var adSlots = ['ad-slot-1'];

    adSlots.forEach(function(slotId) {
        var highestBid = pbjs.getHighestCpmBids(slotId)[0];
        if (highestBid) {
            // Fire nurl callback to notify ad server of win
            if (highestBid.nurl) {
                var nurlWithPrice = highestBid.nurl.replace('${AUCTION_PRICE}', highestBid.cpm || 0);
                fetch(nurlWithPrice, { method: 'GET' }).catch(function() {});
            }

            var adContainer = document.getElementById(slotId);
            var iframe = document.createElement('iframe');
            iframe.style.cssText = 'border:0;width:' + highestBid.width + 'px;height:' + highestBid.height + 'px;';
            adContainer.appendChild(iframe);
            var iframeDoc = iframe.contentWindow.document;
            iframeDoc.write(highestBid.ad);
            iframeDoc.close();
        }
    });
}

Full Example ​

TIP

Below is a minimal complete page example that you can copy and use directly.

html
<!DOCTYPE html>
<html>
<head>
    <title>My Website</title>
    <script async src="https://cdn.jsdelivr.net/npm/prebid.js@latest/dist/not-for-prod/prebid.js"></script>
    <script>
        var pbjs = pbjs || {};
        pbjs.que = pbjs.que || [];

        pbjs.que.push(function() {
            pbjs.addAdUnits([{
                code: 'ad-slot-1',
                mediaTypes: {
                    banner: { sizes: [[300, 250]] }
                },
                bids: [{
                    bidder: 'revosurge',
                    params: { placementId: 'your-placement-id' }
                }]
            }]);

            pbjs.requestBids({
                timeout: 2000,
                bidsBackHandler: function() {
                    var bid = pbjs.getHighestCpmBids('ad-slot-1')[0];
                    if (bid) {
                        // Fire nurl to notify ad server
                        if (bid.nurl) {
                            fetch(bid.nurl.replace('${AUCTION_PRICE}', bid.cpm || 0)).catch(function() {});
                        }
                        var el = document.getElementById('ad-slot-1');
                        var f = document.createElement('iframe');
                        f.width = bid.width;
                        f.height = bid.height;
                        f.style.border = 'none';
                        el.appendChild(f);
                        f.contentWindow.document.write(bid.ad);
                        f.contentWindow.document.close();
                    }
                }
            });
        });
    </script>
</head>
<body>
    <h1>My Website</h1>
    <div id="ad-slot-1"></div>
</body>
</html>

Ad Unit Configuration ​

Prebid.js supports multiple ad formats. Below are the formats supported by the Revosurge Bidder and their configuration.

The most common ad format, suitable for fixed-size display ads.

js
{
    code: 'banner-slot',
    mediaTypes: {
        banner: {
            sizes: [
                [300, 250],   // Medium Rectangle
                [336, 280],   // Large Rectangle
                [728, 90],    // Leaderboard
                [970, 250]    // Billboard
            ]
        }
    },
    bids: [{
        bidder: 'revosurge',
        params: {
            placementId: 'your-placement-id'
        }
    }]
}

Video Ads ​

Suitable for Outstream or Instream video ads.

js
{
    code: 'video-slot',
    mediaTypes: {
        video: {
            playerSize: [640, 480],
            context: 'outstream',        // 'outstream' or 'instream'
            mimes: ['video/mp4', 'video/webm'],
            protocols: [1, 2, 3, 4, 5, 6],
            playbackmethod: [2],          // Auto-play muted
            maxduration: 30
        }
    },
    bids: [{
        bidder: 'revosurge',
        params: {
            placementId: 'your-video-placement-id'
        }
    }]
}

Native Ads ​

Native ads blend with your page content style, providing a better user experience.

js
{
    code: 'native-slot',
    mediaTypes: {
        native: {
            title:   { required: true, len: 80 },
            body:    { required: true, len: 200 },
            image:   { required: true, sizes: [300, 250] },
            icon:    { required: false, sizes: [50, 50] },
            cta:     { required: false },
            sponsoredBy: { required: true }
        }
    },
    bids: [{
        bidder: 'revosurge',
        params: {
            placementId: 'your-native-placement-id'
        }
    }]
}

Multi-Format Ads ​

A single ad slot supports multiple formats, letting the system choose the optimal ad type.

js
{
    code: 'multi-format-slot',
    mediaTypes: {
        banner: { sizes: [[300, 250]] },
        video:  {
            playerSize: [300, 250],
            context: 'outstream',
            mimes: ['video/mp4']
        }
    },
    bids: [{
        bidder: 'revosurge',
        params: {
            placementId: 'your-multi-format-placement-id'
        }
    }]
}

Server-to-Server (S2S) Mode ​

S2S Overview ​

In S2S mode, Prebid.js no longer sends requests directly from the browser to each Bidder. Instead, requests are sent to the Prebid Server, which handles bidding on the server side. Benefits include:

  • Better page performance — Fewer HTTP requests from the browser
  • Lower latency — Server-side network is faster than client-side
  • Better privacy compliance — Less client-side data transmission

S2S Setup ​

js
pbjs.que.push(function() {
    pbjs.setConfig({
        s2sConfig: {
            accountId: 'your-account-id',
            bidders: ['revosurge'],
            adapter: 'prebidServer',
            enabled: true,
            endpoint: 'https://prebid-server.revosurge.com/openrtb2/auction',
            syncEndpoint: 'https://prebid-server.revosurge.com/cookie_sync',
            timeout: 3000
        }
    });
});
ParameterTypeRequiredDescription
accountIdStringRequiredAccount ID assigned by Revosurge
biddersArrayRequiredList of Bidders to use via S2S
adapterStringRequiredMust be 'prebidServer'
enabledBooleanRequiredSet to true to enable S2S
endpointStringRequiredPrebid Server Auction endpoint
syncEndpointStringOptionalCookie Sync endpoint
timeoutNumberOptionalS2S request timeout in milliseconds, default 1000

INFO

In S2S mode, the Ad Unit bids configuration remains the same. Prebid.js will automatically forward requests for Bidders listed in s2sConfig.bidders to Prebid Server.

Ad Server Integration (GAM) ​

If the publisher uses Google Ad Manager (GAM) to manage inventory, the recommended pattern is Prebid.js + S2S + GPT. Revosurge still participates through s2sConfig, then Prebid writes price key-values into GPT, and GAM decides which line item finally serves.

Flow:

  1. Load GPT and Prebid.js, then define the GAM slot on the page.
  2. Call disableInitialLoad() in GPT so the slot does not request too early.
  3. Configure Revosurge through pbjs.setConfig({ s2sConfig }).
  4. After bidding completes, call pbjs.setTargetingForGPTAsync() to write header bidding keys.
  5. Finally call googletag.pubads().refresh() so GAM can return the final ad.

Example: Prebid S2S + GAM Managed ​

html
<!DOCTYPE html>
<html>
<head>
    <title>Prebid S2S + GAM</title>
    <script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
    <script async src="/js/prebid.js"></script>
</head>
<body>
    <div id="div-gpt-ad-1" style="min-width:300px;min-height:250px;"></div>

    <script>
        var PREBID_TIMEOUT = 3000;
        var FAILSAFE_TIMEOUT = 4000;

        window.googletag = window.googletag || { cmd: [] };
        window.pbjs = window.pbjs || {};
        pbjs.que = pbjs.que || [];

        var adUnits = [{
            code: 'div-gpt-ad-1',
            mediaTypes: {
                banner: {
                    sizes: [[300, 250]]
                }
            },
            bids: [{
                bidder: 'revosurge',
                params: {
                    placementId: 'your-placement-id'
                }
            }]
        }];

        googletag.cmd.push(function() {
            googletag.defineSlot('/1234567/example/banner', [[300, 250]], 'div-gpt-ad-1')
                .addService(googletag.pubads());

            googletag.pubads().disableInitialLoad();
            googletag.enableServices();
        });

        pbjs.que.push(function() {
            pbjs.setConfig({
                s2sConfig: {
                    accountId: 'your-account-id',
                    bidders: ['revosurge'],
                    adapter: 'prebidServer',
                    enabled: true,
                    endpoint: 'https://prebid-server.revosurge.com/openrtb2/auction',
                    syncEndpoint: 'https://prebid-server.revosurge.com/cookie_sync',
                    timeout: PREBID_TIMEOUT
                },
                userSync: {
                    iframeEnabled: true,
                    pixelEnabled: true
                }
            });

            pbjs.addAdUnits(adUnits);

            pbjs.requestBids({
                timeout: PREBID_TIMEOUT,
                bidsBackHandler: sendAdServerRequest
            });
        });

        function sendAdServerRequest() {
            if (sendAdServerRequest.called) return;
            sendAdServerRequest.called = true;

            googletag.cmd.push(function() {
                pbjs.que.push(function() {
                    pbjs.setTargetingForGPTAsync(['div-gpt-ad-1']);
                    googletag.pubads().refresh();
                });
            });
        }

        setTimeout(sendAdServerRequest, FAILSAFE_TIMEOUT);
    </script>
</body>
</html>

Bid Parameters ​

The Revosurge Bidder supports the following parameters:

ParameterTypeRequiredDescription
placementIdStringRequiredAd placement ID, obtained after creating a placement in the AdFlow dashboard

Example:

js
bids: [{
    bidder: 'revosurge',
    params: {
        placementId: 'abc123'
    }
}]

Preferred Deal ​

Overview ​

Preferred Deal is a private programmatic buying arrangement (PMP - Private Marketplace) that allows publishers to establish priority trading agreements with specific advertisers or buyers. After configuring Preferred Deals in Prebid.js, the system will prioritize matching these private deals while still participating in open auctions.

Benefits

Preferred Deals typically yield higher CPM while ensuring ad quality and brand safety.

Configuration Methods ​

Prebid.js supports two methods for configuring Preferred Deals:

Method 1: Add dealId to Bidder Params (Simple Method)

Add the dealId parameter directly in the params of bids:

js
bids: [{
    bidder: 'revosurge',
    params: {
        placementId: 'your-placement-id',
        dealId: 'your-preferred-deal-id'  // Add Deal ID
    }
}]

Method 2: Use ortb2Imp to Configure PMP

Use the OpenRTB standard ortb2Imp.pmp object for configuration, supporting richer parameters and multiple deals:

js
{
    code: 'ad-slot-1',
    mediaTypes: {
        banner: {
            sizes: [[300, 250], [336, 280]]
        }
    },
    bids: [{
        bidder: 'revosurge',
        params: {
            placementId: 'your-placement-id'
        }
    }],
    ortb2Imp: {
        pmp: {
            private_auction: 0,  // 0=allow open auction, 1=private deals only
            deals: [{
                id: 'deal-12345',           // Deal ID (required)
                bidfloor: 5.0,              // Floor price (optional)
                bidfloorcur: 'USD',         // Floor currency (optional)
                at: 1,                      // Auction type: 1=first price, 2=second price
                wseat: ['advertiser-seat'], // Allowed buyer seats (optional)
                wadomain: ['advertiser.com'] // Allowed advertiser domains (optional)
            }]
        }
    }
}

PMP Parameters

ParameterTypeRequiredDescription
deals[].idStringRequiredDeal ID, provided by advertiser or SSP
deals[].bidfloorNumberOptionalFloor price; bids below this will be rejected
deals[].bidfloorcurStringOptionalFloor price currency (e.g., USD, CNY), default USD
deals[].atNumberOptionalAuction type: 1=first price, 2=second price
private_auctionNumberOptional0=allow open auction, 1=private deals only, default 0
deals[].wseatArrayOptionalAllowed buyer seat whitelist
deals[].wadomainArrayOptionalAllowed advertiser domain whitelist

Configuration Best Practices ​

  • Use the ortb2Imp method — More standardized and supports advanced features
  • Set private_auction: 0 — Allow Preferred Deals and open auctions to participate together, increasing fill rate
  • Configure multiple Deals — Set up multiple deals for the same ad slot to increase matching opportunities
  • Set reasonable floor prices — bidfloor should be set based on actual conditions; setting it too high will reduce fill rate
  • Combine with S2S — Preferred Deals perform better in Server-to-Server mode

Advanced Configuration ​

Price Granularity ​

Price granularity determines how bid amounts are rounded, affecting the number of Line Items needed and revenue precision.

js
pbjs.setConfig({
    priceGranularity: 'medium'  // 'low' | 'medium' | 'high' | 'auto' | 'dense' | custom
});
LevelRangeIncrement
low$0 - $5$0.50
medium$0 - $20$0.10
high$0 - $20$0.01
auto$0 - $20Varies
dense$0 - $20Varies (denser)

Timeout Settings ​

js
pbjs.setConfig({
    bidderTimeout: 2000   // In milliseconds, recommended 1500-3000
});

INFO

Setting the timeout too short may cause Bidders to not respond in time (lower bid rate). Setting it too long may affect user experience. It's recommended to set it between 1500-3000ms based on your actual network conditions.

User Sync ​

User sync allows Bidders to identify users, improving bidding performance.

js
pbjs.setConfig({
    userSync: {
        iframeEnabled: true,
        pixelEnabled: true,
        syncsPerBidder: 5,
        syncDelay: 3000,          // Delay sync 3 seconds after page load
        filterSettings: {
            iframe: {
                bidders: '*',         // Allow all bidders
                filter: 'include'
            }
        }
    }
});

First Party Data ​

Pass site-level or user-level first-party data to help Bidders bid more accurately:

js
pbjs.setConfig({
    ortb2: {
        site: {
            name: 'My Website',
            domain: 'example.com',
            cat: ['IAB1'],          // Site category
            content: {
                language: 'en'
            }
        },
        user: {
            keywords: 'sports,news'
        }
    }
});

Testing & Debugging ​

Enable Debug Mode

View the complete bidding process in the browser console:

js
pbjs.setConfig({ debug: true });

You can also enable it by adding a URL parameter:

https://your-website.com/?pbjs_debug=true

Using Prebid.js Console Methods

Use the following commands in the browser developer console to inspect the state:

js
// View all bid responses
pbjs.getBidResponses();

// View all winning bids
pbjs.getHighestCpmBids();

// View current configuration
pbjs.getConfig();

// View Ad Units
pbjs.adUnits;

Using the Test Page

We provide an interactive test page S2S Debugger that can be used to:

  • Verify Prebid Server connectivity
  • Test OpenRTB Auction requests/responses
  • Test Prebid.js S2S integration
  • View real-time bidding logs

Common Checklist

Check ItemDescription
Is Prebid.js loaded?Run pbjs.version in the console to check the version
Are Ad Units configured correctly?Check if pbjs.adUnits contains your defined ad slots
Is the Bidder responding?Look for bid requests and responses in the Network panel
Is GAM Targeting set?Check if GAM request parameters include keys like hb_pb
Is the S2S endpoint reachable?Use the test page to verify Prebid Server connectivity

FAQ ​

Q: Why am I not receiving any bids?

Common reasons:

  1. placementId is incorrect or not activated in the AdFlow dashboard
  2. Revosurge Bidder Adapter is not included in the Prebid.js build
  3. Timeout is set too short (at least 2000ms recommended)
  4. Network issues preventing requests from reaching the Bidder

Q: How do I configure multiple ad slots on a page?

Add an object for each ad slot in the adUnits array, with each object's code corresponding to a different div id on the page:

js
var adUnits = [
    { code: 'top-banner',    mediaTypes: { banner: { sizes: [[728, 90]] }},  bids: [{ bidder: 'revosurge', params: { placementId: 'pid-top' }}] },
    { code: 'sidebar-ad',    mediaTypes: { banner: { sizes: [[300, 250]] }}, bids: [{ bidder: 'revosurge', params: { placementId: 'pid-side' }}] },
    { code: 'article-bottom', mediaTypes: { banner: { sizes: [[970, 250]] }}, bids: [{ bidder: 'revosurge', params: { placementId: 'pid-bottom' }}] }
];

Q: How do I improve Fill Rate?

  • Add more supported ad sizes by providing additional sizes options
  • Extend the timeout appropriately
  • Enable User Sync to improve user identification rate
  • Pass first-party data to help Bidders bid more accurately

Q: Why isn't my Preferred Deal matching after configuration?

Possible reasons:

  1. Incorrect Deal ID — Verify that the Deal ID matches exactly what was provided by the advertiser
  2. Floor price set too high — A bidfloor higher than the advertiser's bid will prevent matching
  3. Deal not activated — Contact the advertiser to confirm the deal is activated in their system
  4. Incorrect private_auction setting — Setting it to 1 excludes open auction; recommend setting to 0
  5. Whitelist restrictions — Check if wseat or wadomain are configured correctly

TIP

Enable Debug mode (pbjs.setConfig({debug: true})) to view detailed deal matching logs in the browser console.

Prebid Mobile SDK (In-App) ​

To monetize a native iOS or Android app, use the open-source Prebid Mobile SDK. The SDK sends bid requests from the app to the Revosurge Prebid Server (the same server used by S2S Configuration), then either renders the winning ad itself or passes the bid to your ad server (such as GAM).

Integration methods

  • Prebid-rendered (no ad server) — The SDK runs the auction and renders the winning ad. Simplest option when you don't use an ad server.
  • GAM (bidding only) — The SDK fetches bids and attaches price key-values to the GAM request. GAM decides which ad serves.

TIP

The examples below target Prebid Mobile SDK 3.x. In 3.0, setPrebidServerHost() / Host.createCustomHost() were removed. Pass the Prebid Server URL to initializeSdk() instead.

Prerequisites ​

  • An AdFlow account with an Account ID
  • A Config ID for each ad slot (the stored impression ID on the Revosurge Prebid Server), provided by your Revosurge contact
  • Android: minSdkVersion 21 or higher; iOS: iOS 12.0 or later

Install the SDK ​

groovy
dependencies {
    implementation 'org.prebid:prebid-mobile-sdk:3.+'

    // Only needed for the GAM Prebid-rendered method
    implementation 'org.prebid:prebid-mobile-sdk-gam-event-handlers:3.+'
}
ruby
target 'MyApp' do
    pod 'PrebidMobile'

    # Only needed for the GAM Prebid-rendered method
    pod 'PrebidMobileGAMEventHandlers'
end

iOS also supports Swift Package Manager: add https://github.com/prebid/prebid-mobile-ios as a package dependency.

Initialize the SDK ​

Initialize once at app startup (in Application.onCreate() on Android, or application(_:didFinishLaunchingWithOptions:) on iOS), on the main thread.

kotlin
import org.prebid.mobile.PrebidMobile
import org.prebid.mobile.api.data.InitializationStatus

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        PrebidMobile.setPrebidServerAccountId("your-account-id")
        PrebidMobile.setTimeoutMillis(3000)

        PrebidMobile.initializeSdk(
            applicationContext,
            "https://prebid-server.revosurge.com/openrtb2/auction"
        ) { status ->
            if (status == InitializationStatus.SUCCEEDED) {
                Log.d("Prebid", "SDK initialized")
            } else {
                Log.e("Prebid", "SDK init error: $status ${status.description}")
            }
        }
    }
}
swift
import PrebidMobile

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Prebid.shared.prebidServerAccountId = "your-account-id"
    Prebid.shared.timeoutMillis = 3000

    Prebid.initializeSDK(serverURL: "https://prebid-server.revosurge.com/openrtb2/auction") { status, error in
        if status == .succeeded {
            print("Prebid SDK initialized")
        } else if let error = error {
            print("Prebid SDK init error: \(error.localizedDescription)")
        }
    }
    return true
}
ParameterRequiredDescription
Prebid Server URLRequiredhttps://prebid-server.revosurge.com/openrtb2/auction
Account IDRequiredAccount ID assigned by Revosurge (same as accountId in S2S)
TimeoutOptionalBid request timeout in milliseconds, recommended 2000-3000

Banner (Prebid-rendered) ​

Without an ad server, create a BannerView with the Config ID and size, add it to your layout, and call loadAd().

kotlin
import org.prebid.mobile.AdSize
import org.prebid.mobile.api.rendering.BannerView

val bannerView = BannerView(context, "your-config-id", AdSize(320, 50))
bannerView.setAutoRefreshDelay(30)   // Seconds, optional
adContainer.addView(bannerView)
bannerView.loadAd()

// Call bannerView.destroy() in onDestroy() to release resources
swift
import PrebidMobile

let bannerView = BannerView(
    frame: CGRect(origin: .zero, size: CGSize(width: 320, height: 50)),
    configID: "your-config-id",
    adSize: CGSize(width: 320, height: 50)
)
bannerView.delegate = self
bannerView.refreshInterval = 30   // Seconds, optional
adContainer.addSubview(bannerView)
bannerView.loadAd()

Interstitial (Prebid-rendered) ​

Load the interstitial first, then show it after it has loaded.

kotlin
import org.prebid.mobile.api.exceptions.AdException
import org.prebid.mobile.api.rendering.InterstitialAdUnit
import org.prebid.mobile.api.rendering.listeners.InterstitialAdUnitListener

val interstitial = InterstitialAdUnit(context, "your-config-id")
interstitial.setInterstitialAdUnitListener(object : InterstitialAdUnitListener {
    override fun onAdLoaded(adUnit: InterstitialAdUnit?) {
        adUnit?.show()
    }
    override fun onAdFailed(adUnit: InterstitialAdUnit?, e: AdException?) {
        Log.e("Prebid", "Interstitial failed: ${e?.message}")
    }
    override fun onAdDisplayed(adUnit: InterstitialAdUnit?) {}
    override fun onAdClicked(adUnit: InterstitialAdUnit?) {}
    override fun onAdClosed(adUnit: InterstitialAdUnit?) {}
})
interstitial.loadAd()
swift
import PrebidMobile

class ViewController: UIViewController, InterstitialAdUnitDelegate {
    var interstitial: InterstitialRenderingAdUnit!

    func loadInterstitial() {
        interstitial = InterstitialRenderingAdUnit(configID: "your-config-id")
        interstitial.delegate = self
        interstitial.loadAd()
    }

    func interstitialDidReceiveAd(_ interstitial: InterstitialRenderingAdUnit) {
        interstitial.show(from: self)
    }

    func interstitial(_ interstitial: InterstitialRenderingAdUnit,
                      didFailToReceiveAdWithError error: Error?) {
        print("Interstitial failed: \(error?.localizedDescription ?? "")")
    }
}

GAM Integration (Bidding Only) ​

If your app already uses Google Ad Manager, use BannerAdUnit + fetchDemand(): Prebid fetches bids from the Revosurge Prebid Server and writes hb_* key-values into the GAM request, then GAM decides the final ad. You need matching Prebid line items in GAM (same as the web GAM integration).

kotlin
import org.prebid.mobile.BannerAdUnit

val adUnit = BannerAdUnit("your-config-id", 300, 250)

val gamView = AdManagerAdView(context).apply {
    adUnitId = "/1234567/example/banner"
    setAdSizes(AdSize.MEDIUM_RECTANGLE)
}
adContainer.addView(gamView)

val request = AdManagerAdRequest.Builder().build()
adUnit.fetchDemand(request) { resultCode ->
    // Load GAM regardless of the Prebid result
    gamView.loadAd(request)
}
swift
import PrebidMobile
import GoogleMobileAds

let adUnit = BannerAdUnit(configId: "your-config-id", size: CGSize(width: 300, height: 250))

let gamBanner = AdManagerBannerView(adSize: AdSizeMediumRectangle)
gamBanner.adUnitID = "/1234567/example/banner"
gamBanner.rootViewController = self
adContainer.addSubview(gamBanner)

let request = AdManagerRequest()
adUnit.fetchDemand(adObject: request) { resultCode in
    // Load GAM regardless of the Prebid result
    gamBanner.load(request)
}

INFO

For GAM interstitial, rewarded, video, and native formats, the GAM Prebid-rendered method, and other ad servers (AdMob, AppLovin MAX), see the official Prebid Mobile docs. Keep using the Revosurge Prebid Server URL, Account ID, and Config IDs in all of them.

Testing & Debugging ​

kotlin
PrebidMobile.setPbsDebug(true)                    // Return debug info in the Prebid Server response
PrebidMobile.setLogLevel(PrebidMobile.LogLevel.DEBUG)
swift
Prebid.shared.pbsDebug = true                     // Return debug info in the Prebid Server response
Prebid.shared.logLevel = .debug

Common Checklist

Check ItemDescription
Did SDK initialization succeed?The initializeSdk callback should return SUCCEEDED; otherwise check the server URL and network
Is the Account ID correct?It must match the Account ID assigned by Revosurge
Is the Config ID correct?An unknown Config ID returns no bids; confirm it with your Revosurge contact
Does the size match?The ad size must match the size configured for the Config ID
Remove debug settings before releaseTurn off pbsDebug and set a lower log level for production builds

Q: Why am I not receiving any bids in the app?

  1. The SDK was not initialized, or the ad was requested before initialization finished
  2. The Account ID or Config ID is wrong
  3. The timeout is too short (at least 2000ms recommended)
  4. On iOS, the App Tracking Transparency prompt was not shown, so no IDFA is sent, which can lower fill rate

For technical support, please contact the Revosurge technical team.