Post Click Conversions on Private Auction Wins
Post Click Conversions on Private Wins
Test Home
Overview
Here we go over a solution that can enable maintenance of our current functionality for post click attribution, during the current "intermediate sandbox" stage.
Prereqs
Have your chrome setup for PS testing.
Problem
When a slot is won by a Private Auction, the creative must render in an iframe that gets the renderUrl from the IG as it was declared at "Join Time", which in particular means that no auction info such as a Click ID can be put on the iframe src directly.
Goal
The goal for a solution is to be able to get a Click ID into a click handler for the rendered creative in the creative iframe that renders the renderUrl of the private win.
Note, that here we'll assume a "click ID" could effectively be an auction_id, or something that uniquely identifies an auction.
Solution Overview
This solution will work for cases for when the private auction wins and the SSP and DSP share the same origin.
As is this will not work for cases where the SSP and DSP don't share the same origins, presumably b/c they are different entities. More work to be done to get that working.
Since this solution applies only to cases where the SSP and DSP are the same (or to be more precise, sharing the same IG origin) we'll refer to the "ad tech".
This solution relies on:
- The availability of private rendering in an iframe till >= 2026. (not sure yet how ff impacts this).
- Knowing that the origin of the rendering creative is the same as the origin of the Ad Tech as used in the Auction Runner.
- There being at most 1 private auction for a corresponding contextual auction per slot.
The steps at a high level are:
- Run the contextual auction on page, deciding to run the private auction, and creating a Private Auction Runner IFrame, putting query parameters in for relevant information, in particular Click ID.
- The Auction Runner IFrame runs the private auction, then renders the private winner in an iframe with opaque URN.
- The server side HTML rendering includes JS to grab the Click ID from (1), which works due to same origin.
- The click handler of the rendered image is decorated with the Click ID.
- When the user clicks on the image and is taken to the the Advertisers page, code on that page stores the Click ID in local storage.
- When the conversion occurs that Click ID is looked up, and passed to the pipeline for joining.
Test
Overview
See domain structure to understand the domains used here, with the only difference being that we use pst-ssp as the domain for the Ad Tech, since we are treating the SSP and DSP as the same for the scope of this solution.
Steps
Let's walk through the test. As you go through the steps and click the links, they will open in new tabs. The new tabs will give their own descriptions of what they are doing and display test results as makes sense.
- Setup: First we Clear the Ad Tech Data Pipeline,
which for ease of inspection will result in a redirect to the
imp and pixel log viewer, which should show
no entries for impressions or pixels, so we start with a clean pipeline (no tricks!).
- Publisher Page - Contextual: Next we hit the
publishers content
which invokes the Ad Tech code on page:
- We start by creating a client_request_id which will be used to unify all auctions run here, contextual or private.
- We then call our contextual auction, including the client_request_id for logging server side.
- The contextual auction returns it's results, including an auction_id to identify each individual auction within a multi tag request.
- The Ad Tech code decides that the winning contextual bid is not high enough, and so it will run a private auction.
You can see the client side JS that runs 2a, 2b, and 2d here. The server side JS that "runs the contextual auction" in 2c is here.
- Publisher Page - Private Auction: To run a private auction for a given slot, we execute the following steps:
- We load a "Private Auction Runner" in an iframe. The src of the Runner is:
- Same origin with the Ad Tech (which importantly for later will also be same origin for the rendering of the creative that wins the private auction).
- Has query params for a few categories of information:
- Join Keys: client_request_id and the corresponding slot auction_id.
- BidRequest: tag ID, size, TLD.
- ContextualResult: winning bid info for comparison.
- The Runner then takes the information from the query params and invokes the Private Auction. The BidRequest information and client_request_id and "corresponding auction_id" are then put into the auctionSignals (for use later in worklet reporting) as well as the ContextualResult info for bid comparison.
- In this case the Private Bid beats the Contextual Bid, and so the code renders the winning creative in an iframe using the opaque URN returned by the private auction.
- We load a "Private Auction Runner" in an iframe. The src of the Runner is:
- Publisher Page - Render Private Win: Since the winning creative is same origin as the Ad Tech, and therefore the Private Auction Runner IFrame, JS within
the creative IFrame can read the href of the Private Auction Runner IFrame. This gives it access to all the query params
mentioned in step 3.a.ii. It also has access to the pixel ID being targeted by the winning creative via it's own href.
So we now can:
- Fire the impression tracker for the private auction using the reportWin API, which will include the client_request_id, and corresponding contextual auction id, in the auctionSignals we created in 3b.
- The browser renders the private winner renderUrl in a new iframe, which results in the renderUrl call hitting
our server which dynamically creates the html document, including JS, using information from the parent iframe (like client_request_id) and also the
rendering iframe (like the creative and pixel):
- Inject the ultimately targeted actual creative url.
- Add a click handler to the image which includes the client_request_id, auction_id, and pixel_id.
Again you can verify that the "state of the pipeline" has the corresponding imp tracker log but no pixel logs.
The reportWin function that is invoked by the fledge framework in 4a is here. The client side JS that runs 4b is here. The fastify handler that fills out the template is here (look for /conversions/post-click/private-win/render).
- Advertiser Page - Landing Time: We then click on the image which takes us to the advertisers site,
where code on page takes the client_request_id, auction_id, and pixel_id from 4.b.ii and stores that information in
local storage.
The client side JS that runs step 5 is here.
- Advertiser Page - Conversion Time: Finally, after much shopping, we hit the conversion page
and click the "buy and pay money" button for the advertised product. That button is attached
to a pixel, that just so happens to be the one attached to the winning creative. The code on page
then forms it's normal pixel tracker URL using those variables, and sends that back to the Ad Tech Data Pipeline.
here, which could then be used for transaction resolution.
The client side JS that runs step 6 is here.
- Tear Down/Clean Up: Optionally we clear the value from local storage (might be worth keeping around, no matter to this test).
The clean up is the last line here.
Results
You can see expected results here.
Scope and Limitations
Scope
- This solution works for private wins. It will also work for contextual wins, so we can in theory keep the flow the same across both; however, this solution being more complex will result in more challenging debugging, and more moving parts means more things can break, so we'll want to think carefully about that (along with the functional limitation of DSP/SSPs as discussed).
- The demo is set up for a single tag page but the approach will work for multi tag pages, i.e. a case where we wanted The footwork as coded here should work, although it is not tested here, to be explored. Also be aware that multi tag support remains logistically challenged by fledge design.
Limitations
The major limitation is that this will not work for DSPs and SSPs that are different entities.