title | sidebar_position | more_data | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Instream video ad in a Protected Audience sequential auction setup |
1 |
|
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
:::info
This demo is a proof of concept created for illustrative purposes only, and should not be taken as a recommendation or standardized implementation of this use case.
:::
☝️ Use the tabs to navigate to other sections in this document
If you have any questions and comments for this instream video ad demo, use the instream video ad demo post in the Discussions tab.
This demo shows one of the ways that VAST XMLs can be handled in a Protected Audience sequential auction setup when the ad is rendered in an iframe. In this demo, a pre-roll video ad has been implemented, but the same mechanism can be used to render other video ad types that use the VAST XML standard, such as mid-roll and post-roll instream video ads.
The following features have been implemented:
- The SSP's VAST XML wraps around the DSP's VAST URI
- A unique ID is appended to the reporting URLs in the VAST XML
- This unique ID is also propagated to the header bidding auction, ad server auction, and every Protected Audience auction worklet
- The finalized VAST is messaged out of the creative iframe to the video player
Note that this mechanism does not work with Fenced Frames, but Protected Audience allows iframe usage until at least 2026.
- Chrome > v120 (Open chrome://version to look up your current version)
- Enable Privacy Sandbox APIs (Open
chrome://settings/privacySandbox
to enable this setting)
We are in the process of enrolling the new endpoints. Until then, visit chrome://flags/#privacy-sandbox-enrollment-overrides
and add the following
origins to the
Privacy Sandbox Overrides textbox.
Origins for the enrollment override:
https://privacy-sandbox-demos-dsp-a.dev,
https://privacy-sandbox-demos-dsp-b.dev,
https://privacy-sandbox-demos-ssp-a.dev,
https://privacy-sandbox-demos-ssp-b.dev,
https://privacy-sandbox-demos-ad-server.dev
The query param that enables the video ad demo on the advertiser and publisher sites is ?auctionType=multi
.
- Navigate to advertiser site: https://privacy-sandbox-demos-shop.dev/items/1f45e?auctionType=multi
- Navigate to publisher site: https://privacy-sandbox-demos-news.dev/?auctionType=multi
- Click ‘Play video’ when the button is ready
The finalized VAST XML is messaged out of the iframe to the publisher page, and that VAST is passed to the video player. The finalized VAST contains the DSP+SSP wrapped VAST and a unique ID appended to URLs in VAST. The server-side VAST transformation is conducted by the SSP.
When the DSP adds the render URL, a query param macro is added that specifies the SSP's VAST serving endpoint (i.e.
https://DSP-A.com/ad.html?sspVastUrl=%%SSP_VAST_URL%%
). When the SSP constrcuts the component auction config, the macro replacement is defined
(deprecatedReplaceInURN: { '%%SSP_VAST_URL%%': 'https://SSP-A.com/vast' }
). When the ad is rendered, the macro is replaced by the value defined by
the seller in the component auction config.
When the ad is rendered, the creative code parses the SSP's VAST URL from the query params, and makes a request to that endpoint, with the DSP VAST URI and the unique ID set as query params. On the server, the SSP wraps its own VAST XML around the DSP's VAST URI, and appends the unique ID to the URLs in the VAST. The SSP's server responds to the creative's request with the finalized VAST. The finalized VAST is post-messaged from the iframe to the publisher page, and that VAST is passed to the video player.
The deprecatedReplaceInURN()
function allows macros to be replaced in the winning ad's render URL.
Example:
deprecatedReplaceInURN('%%SOME_MACRO%%', 'some-value')
This mechanism can be used to pass information into the creative iframe, like the winning SSP's VAST URL. However, since this feature will become available M123, this demo adds a render URL for each SSP, then filter the render URLs when generating bids, as a temporary mechanism for passing the SSP VAST URL to the creative.
- Publisher (News site)
- Advertiser (Shoping site)
- Ad Server / Top-Level seller (Ad server)
- Component Seller A (SSP-A)
- Component Seller B (SSP-B)
- Demand Side Platform A (DSP-A)
- Demand Side Platform B (DSP-B)
- Render a pre-roll video ad that plays before the publisher's video plays
- The VAST of the pre-roll ad contains the SSP's VAST and DSP's VAST
- A unique ID is appened to the reporting URLs in the VAST
This documentation and demo assume that the reader is familiar with the sequential auction setup that combines contextual auctions (header bidding and ad server auctions) and a Protected Audience auction.
- Buyer adds the render URL of the video creative in the interest group
- The render URL includes a macro for the VAST location of the winning component SSP (
%%SSP_VAST_URL%%
)
- The render URL includes a macro for the VAST location of the winning component SSP (
- Seller adds the macro replacement configuration in the component auction config
- Uses
deprecatedReplaceInURN()
function which will become available for component sellers in M123 - Until M123, as a temporary mechanism, in this demo, the buyer adds a render URL for each SSP in the interest group
- Uses
- On an auction win, the browser replaces the
%%SSP_VAST_URL%%
macro with the SSP's VAST reporting endpoint - When the ad is rendered in an iframe, the creative code parses SSP's VAST URL, and makes a request with the DSP's VAST URI and the unique ID
- On the server, the SSP wraps its own VAST around the DSP's VAST URI and appends the unique ID to the URLs in the VAST
- The creative receives the response from the SSP's server that contains the finalized VAST XML, and post-messages it to the ad server library code on the publisher's page
- The ad server lib passes the VAST XML to the video player
This is the time period when the DSP configures the interest group, and sets the SSP VAST macro in the render URL.
sequenceDiagram
autonumber
Title: Interest group time
actor User
participant Browser
participant Advertiser
participant Publisher
participant HB as Header <br>Bidding
participant TopLevel as Top-level seller <br>(Ad server)
participant AS as Ad server
participant SSPA as SSP-A
participant SSPB as SSP-B
participant DSPA as DSP-A
participant DSPB as DSP-B
User ->> Advertiser: Visit advertiser page
Advertiser ->> DSPA: Load DSP-A tag
Advertiser ->> DSPB: Load DSP-B tag
DSPA ->> Browser: Add user to Interest Group<br>Set a render URL with SSP VAST macro (%%SSP_VAST_URL%%)
DSPB ->> Browser: Add user to Interest Group<br>Set a render URL with SSP VAST macro (%%SSP_VAST_URL%%)
This is the time period when the seller defines the macro substitution in the component auction config
sequenceDiagram
autonumber
Title: Header bidding time
actor User
participant Browser
participant Advertiser
participant Publisher
participant HB as Header <br>Bidding
participant AS as Ad server
participant SSPA as SSP-A
participant SSPB as SSP-B
participant DSPA as DSP-A
participant DSPB as DSP-B
User ->> Publisher: Visit publisher page
Publisher ->> HB: Start header bidding auction
HB ->> SSPA: Request header bid and component auction config<br>VAST macro replacement is defined in the component auction config<br> (deprecatedReplaceInURN())
SSPA ->> DSPA: Request header bid and per-buyer signals
SSPA ->> HB: Respond with the header bid and component auction config
HB ->> SSPB: Request header bid and component auction config<br>VAST macro replacement is defined in the component auction config<br> (deprecatedReplaceInURN())
SSPB ->> DSPB: Request header bid and per-buyer signals
SSPB ->> HB: Respond with the header bid and component auction config
HB ->> AS: Pass the header bidding auction result to the ad server client-side library
This is the time period when the VAST is transformed and passed to the creative.
We will skip the ad server auction and the Protected Audience auction since they are irrelevant to the video ad rendering flow. To learn more about the rest of the auctions, see the sequential auctions setup article.
The following sequence begins after a video ad winner has been chosen from the Protected Audience auction. Also, only the winning DSP and SSP are shown in the diagram, since other multi-seller auction DSPs/SSPs are no longer part of the auction.
sequenceDiagram
autonumber
Title: Ad rendering time
participant Publisher
participant Browser
participant AS as Ad server lib <br>(Top-level seller)
participant VP as Video player SDK
participant DSP as DSP's ad iframe
participant SSP as SSP's server
Browser ->> Browser: Choose Protected Audience auction winner
Browser ->> Browser: Substitute %%SSP_VAST_URL%% macro <br>with the value provided by the winning SSP
Browser ->> AS: Return winning PA video ad (opaqueURN)
AS ->> Browser: Render winning ad in an iframe
Browser ->> DSP: Fetch video ad<br>The render URL contains the SSP VAST URL query param
DSP ->> DSP: Inside the ad iframe, parse the SSP VAST URL query param
DSP ->> SSP: Inside the ad iframe, a request is sent to the SSP's VAST endpoint<br>The DSP's VAST URI and unique ID are sent in the request
SSP ->> SSP: On the server, SSP transforms the VAST<br>The VASTs are wrapped, and a unique ID is appended to the URLs in the VAST
SSP ->> DSP: The finalized VAST is sent to the creative iframe
DSP ->> AS: The VAST is post-messaged to the ad sever lib (top-level seller)
AS ->> VP: The VAST is passed to the video player
VP ->> Publisher: The instream video ad is rendered
In another approach, the SSP can provide the render URL. The DSP sets the following render URL in the IG:
const interestGroup = {
// ...
ads: [
{
renderUrl: 'https://privacy-sandbox-demos-ssp-a.dev/video-ad.html?dspVastUri=https://privacy-sandbox-demos-dsp-a.dev/preroll.xml',
metadata: {
seller: 'ssp-a'
}
},
{
renderUrl: 'https://privacy-sandbox-demos-ssp-b.dev/video-ad.html?dspVastUri=https://privacy-sandbox-demos-dsp-a.dev/preroll.xml',
metadata: {
seller: 'ssp-b'
}
},
]
}
- The render URL points to the SSP’s video ad serving endpoint, and the DSP’s VAST URI is added as query params.
- The SSP is now responsible for serving the actual ad that is rendered inside the iframe, and it contains the SSP VAST XML that wraps the DSP VAST URI.
- To support multiple SSPs, the buyer adds a render URL for each SSP. During the bid generation time, the buyer can filter the ads object and return a render URL for the matching seller.
- When that ad wins the auction, the browser makes a request to the render URL which is the SSP's ad serving endpoint
/video-ad.html
with the DSP's VAST URI set in the query params - SSP’s HTML document is rendered in the ad iframe and parses the DSP's VAST URI from the query params
- The code inside SSP's video-ad.html wraps the DSP's VAST URI with its own VAST
- The finalized VAST XML is post-messaged out of the creative frame to the video player
const interestGroup = {
// ...
ads: [{
renderUrl: 'https://privacy-sandbox-demos-dsp-a.dev/html/video-ad-creative.html?sspVastUrl=%%SSP_VAST_URL%%'
}]
}
During render time, the browser replaces the %%SSP_VAST_URL%%
macro defined in the render URL with the value that the winning SSP provides in the
component auction config.
Since the component seller support for deprecatedReplaceInURN()
won't be available until M123, we are using a temporary mechanism to pass the SSP's
VAST URL to the creative iframe.
In the interest group config, the buyer adds the render URL for each seller, and also note the seller in metadata.
const interestGroup = {
// ...
ads: [
{
renderUrl: 'https://privacy-sandbox-demos-dsp-a.dev/html/video-ad-creative.html?sspVastUrl=https://privacy-sandbox-demos-ssp-a.dev/vast',
metadata: {
adType: 'video',
seller: 'https://privacy-sandbox-demos-ssp-a.dev/'
}
},
{
renderUrl: 'https://privacy-sandbox-demos-dsp-a.dev/html/video-ad-creative.html?sspVastUrl=https://privacy-sandbox-demos-ssp-b.dev/vast',
metadata: {
adType: 'video',
seller: 'https://privacy-sandbox-demos-ssp-b.dev/'
}
},
]
}
Then during bid generation the buyer filters out the render URLs that are not for the component auction seller:
function generateBid(browserSignals) {
const {seller} = browserSignals;
return {
render: ads.find(ad => ad.metadata.seller.includes(seller))
}
}
- Code: Bid generation
This is a temporary mechanism that will be replaced by deprecatedReplaceInURN()
in M123.
In a sequential auction setup, the header bidding is executed first, and the sellers have the opportunity to respond to the header bidding request with the component auction config if they want to partcipate in the subsequent Protected Audience auction.
In the component auction config, the SSP defines the macro substitution:
const componentAuctionConfig = {
// ...
deprecatedReplaceInURN: {
'%%SSP_VAST_URI%%': 'https://privacy-sandbox-demos-ssp-a.dev/vast',
}
}
When the ad is rendered in an iframe, the creative code parses the query params, and sends a request to the SSP's VAST URL.
function parseSspVastUrl() {
const url = new URL(window.location.href);
return url.searchParams.get('sspVastUrl');
}
async function fetchVastFromSsp(sspVastUrl, auctionId) {
const requestUrl = new URL(sspVastUrl);
requestUrl.searchParams.append(AUCTION_ID_QUERY_PARAM, auctionId);
requestUrl.searchParams.append(DSP_VAST_URI_QUERY_PARAM, encodeURIComponent(dspVastUri));
const response = await fetch(requestUrl);
const vastXml = await response.text();
return vastXml;
}
On the server, the SSP wraps its own VAST around the DSP's VAST URI and appends the unique ID to the URLs in the VAST
function transformVast(sspVast, dspVastUri, auctionId) {
const vastWithDspUri = sspVast.replace('%%DSP_VAST_URI%%', decodeURIComponent(dspVastUri));
const vastWithAuctionId = vastWithDspUri.replaceAll('%%AUCTION_ID%%', auctionId);
return vastWithAuctionId;
}
app.get('/vast', async (req, res) => {
const {dspVastUri, auctionId} = req.query;
const sspVast = await readFile(path.resolve(__dirname + '/public/vast/preroll.xml'), 'utf8');
const finalizedVast = transformVast(sspVast, dspVastUri, auctionId);
res.type('application/xml');
res.send(finalizedVast);
});
- Code: SSP server-side transformation
- Code: SSP's VAST wrapper
Once the creative receives the finalized VAST from the SSP, it is post-messaged out of the frame to the top-level frame:
function sendVastToParentFrame(vastText) {
const {0: containerFrame} = window.top.frames;
containerFrame.top.postMessage(vastText, '*');
}
The ad server client-side library sets up an event-listener for a message from the frame, and passes the received VAST to the video player.
window.addEventListener('message', ({ data }) => {{
setUpVideoPlayer(data);
});