FLEDGE has been renamed to Protected Audience API. To learn more about the name change, read the blog post.
Author:
Rashmi Rao, Google Privacy Sandbox
Bidding and Auction services outlines a way to allow Protected Audience auctions to take place in a trusted execution environment (TEE) hosted on a supported cloud platform.
This explainer describes the system design for event level reporting using the Bidding and Auction services. For high level design, ad tech specifications, API, refer to the Bidding and Auction services explainer.
The seller and the winning buyer each have an opportunity to generate URLs for event-level reporting based on an auction outcome.
Reporting with Bidding and Auction services is planned to be supported in multiple phases.
Reporting support with Bidding and Auction services | Timeline |
Win reporting for Single Seller Auctions | LAUNCHED |
Win reporting for Device Orchestrated Multi Seller Auctions. | LAUNCHED |
Win reporting for Server Orchestrated Multi Seller Auctions. | Jan 2025 |
Private Aggregation API | June 2025 |
This document details the design to generate and ping win reporting URLs with
the Bidding and Auction services for single-seller auctions. The reporting URLs
are generated from JavaScript using the reportResult()
function provided by the
seller and reportWin()
function provided by the buyer on the Auction service.
Final pings to the URLs happen on the client.
This design supports win reporting on Android and Chrome. The design also supports fenced frame reporting, also known as interaction reporting or beacon reporting.
A Bidding and Auction service enables processing Protected Auction requests on
the server. Sellers provide the code required for scoring ads and reporting URL
generation using the scoreAd()
and reportResult()
functions. Buyers provide the
code for generating the bids and reporting using the generateBid()
and
reportWin()
functions. The flow of Bidding and Auction services is as follows:
- The seller sends a
SelectAd
request to the SellerFrontEnd service. - The service orchestrates requests in parallel to the buyers' BuyerFrontEnd services for Protected Auction bidding.
- The buyers generate bids and return the bids to the seller.
- The SellerFrontEnd service calls the Auction service with the bids and scoring signals obtained from the seller's Key-Value service.
- The Auction service scores the bids and determines the winner.
- The Auction service returns the winning ad to the SellerFrontEnd service.
- The SellerFrontEnd service encrypts the auction result and returns a
SelectAdResponse
to the seller's ad service. - Seller's ad service sends the encrypted auction result back to the client.
After scoring ads and determining a winner, the reportResult() and reportWin() functions generate the reporting URLs. The client must ping these URLs after an ad is rendered.
The seller-provided endpoint for scoreAd()
contains the reportResult()
function.
reportResult(auctionConfig, sellerReportingSignals){
…
return signalsForWinner
}
The arguments for reportResult()
are described in the following table:
Argument |
JSON Object subfield |
Description |
auctionConfig |
auctionSignals |
Contextual signals passed from seller's ad service to the SellerFrontEnd service in SelectAdRequest.AuctionConfig |
signalsForWinner |
Contextual signals passed from seller's ad service to the SellerFrontEnd service in SelectAdRequest.AuctionConfig |
|
sellerReportingSignals |
interestGroupOwner |
DSP / buyer domain. |
renderURL |
Ad render URL of winning bid. | |
bid |
Value of bid that scored highest. | |
desirability |
Score of winning bid. Note: Winning bid refers to the bid with the highest score. |
|
highestScoringOtherBid |
This is the value of a bid with the second highest score in the auction. This value represents eCPM (effective cost per thousand impressions), valued in US dollars. Note:
|
|
topWindowHostName |
This is the host name of the publisher. |
The reportResult()
function returns signalsForWinner, which is used as input for
the buyer's reportWin()
function.
The buyer-provided endpoint for generateBid()
is expected to contain the
reportWin()
function.
reportWin(auctionSignals, perBuyerSignals, signalsForWinner, buyerReportingSignals) {
...
}
The arguments for `reportWin() are described in the following table:
Argument |
JSON Object Subfield |
Description |
auctionSignals |
Contextual signals passed from the seller's ad service to the
SellerFrontEnd service in SelectAdRequest.AuctionConfig . |
|
signalsForWinner |
Signals returned by seller's ReportResult() function execution. |
|
perBuyerSignals |
Contextual signals passed from the seller's ad service to the SellerFrontEnd service in SelectAdRequest.AuctionConfig . This is the buyer signals for the winning buyer. |
|
buyerReportingSignals |
seller |
Seller's origin. This will be passed to the Auction service from the SellerFrontEnd service. |
adCost |
An optional field returned by generateBid() , rounded to fit into a floating point number with an 8 bit mantissa and 8 bit exponent for reporting. |
|
interestGroupName |
The name of the interest group corresponding to the highest scoring bid. | |
madeHighestScoringOtherBid |
This is set to true if the winning buyer was the only buyer that made bids with the second highest score. | |
recency |
Duration of time (in minutes) from when this device joined this interest group until now. This is passed by the client. | |
modelingSignals |
Sent as an output from the generateBid() function. This is expected to be a single integer value. |
|
joinCount |
The number of times the given device has joined the same interest group in the last 30 days, while the interest group has been continuously stored. |
Note:
buyerReportingSignals
additionally contains all the signals present in thesellerReportingSignals
.- The
recency
,modelingSignals
andjoinCount
fields are noised using the noising scheme described here. The parametersbid
,desirability
andadCost
will be rounded to a floating point number with an 8 bit mantissa and 8 bit exponent. Currently the noising can be enabled by setting ENABLE_REPORT_WIN_INPUT_NOISING to true. However, this flag will be removed and noise will be applied by default in B&A from Beta 2. - The
bid
,score
, andadCost
fields are stochastically rounded. browserSignals
in the signature of on-device implementation has been renamed tosellerReportingSignals
for reportResult andbuyerReportingSignals
for reportWin. The reason we changed the name of the argument is because this is actually reporting signals that is not generated by the browser in case of B&A but by the TEE based Auction server based on different parameters. The contents of browserSignals (for on-device) and seller/buyerReportingSignals are the same.- The feature support for buyer_and_seller_reporting_id and selectable_buyer_and_seller_reporting_id in ad properties were recently introduced with on-device Protected Audience auctions. These signals are expected to be used instead of interestGroupName if present. For the server side implementation, these fields will need to be fetched from an AdTech's Key/Value server so that the payload (encrypted ProtectedAuctionInput) from the device doesn't increase. This requires a detailed design to ensure there is no significant increase in latency and costs for the Adtechs.
In the short-term, reportResult()
and reportWin()
reporting are run by calling a
sendReportTo()
API. The sendReportTo()
function can be called at most once
during a function's execution. Eventually, reporting will also support the
Private Aggregation API.
The executable code in Roma contains a sendReportTo()
function which takes a single string argument representing a URL:
sendReportTo = function(url){
// Stores the url in a global variable
}
Ad techs can register URLs that correspond to events or beacons when
reportResult()
and reportWin()
are run. These beacons can be used to trigger
pings using the reportEvent
API on the client.
The beacons can be registered by calling the registerAdBeacon()
function,
which will be a part of the executable code in Roma:
registerAdBeacon = function(interactionUrlMap){
// Stores the map to a global variable.
}
The input to registerAdBeacon
is expected to be a JSON object with a mapping
of the event string to a corresponding URL.
The high level flow for reporting is as follows:
- The client and buyer send the required signals for reporting to the The SellerFrontEnd service.
- The SellerFrontEnd service sends the required signals to run
reportResult()
andreportWin()
to the Auction service - Auction service executes the
reportResult()
andreportWin()
functions in Roma and returns the reporting URLs to The SellerFrontEnd service. - The SellerFrontEnd service encodes the reporting URLs using CBOR encoding for
both buyer and seller and adds it to the
SelectAdResponse
. ThereportResult()
andreportWin()
functions are executed serially in a single dispatch call after the winner has been determined. - Clients use these URLs to ping the buyer and seller reporting endpoints after the ad is rendered.
The inputs to the reporting functions include large signals such as:
- Auction config
- Per-buyer signals
The response payload will significantly increase (potentially by 30kb or more) if reporting URL generation is performed on the client, leading to an increase in the cost and latency. Server-generated reporting URLs are lightweight and don't affect the response payload size.
server?
The reporting function execution can happen only after the ads are scored and
the winner is determined. The reportWin()
function call depends on an output of
the reportResult()
function called signalsForWinner()
. If reportWin()
needs to
be executed on the buyer's Bidding service, an RPC from the SellerFrontEnd
service to BuyerFrontEnd service is needed to fetch the URLs. This adds
significant latency on the critical response path (>50ms) and additional network
costs to both buyer and seller.
buyerReportingId is one of the interest group attributes which is input to reportWin via buyerReportingSignals. If set, the interestGroupName in buyerReportingSignals will not be set. The buyerReportingId is expected to be returned in the response from generateBid for every bid. This value is not obtained from the device due to payload optimization.
GenerateBid() API changes
generateBid(interestGroup, auctionSignals, perBuyerSignals,
trustedBiddingSignals, browserSignals, directFromSellerSignals) {
...
return {'adCost': …,
'bid': …,
'bidCurrency': …,
'render': …,
'adComponents': [...],
'allowComponentAuction': …,
'modelingSignals': …,
'buyerReportingId': “...”};
}
The reporting URLs and beacon URL map for both the seller and the buyer are sent
to the client in a SelectAdResponse
.
These URLs are CBOR-encoded and encrypted before sending the response from the SellerFrontEnd service.
Message AdScore {
...
// The reporting URLs registered during the execution of reportResult() and
// reportWin().
WinReportingUrls win_reporting_urls = 10;
...
std::string buyer_reporting_id = 19;
}
message WinReportingUrls {
message ReportingUrls {
// The url to be pinged for reporting win to the buyer or seller.
string reporting_url = 1;
// The map of (interactionKey, URI).
map<string, string> interaction_reporting_urls = 2;
}
// The reporting URLs registered during the execution of
// reportWin(). These URLs will be pinged from the client.
ReportingUrls buyer_reporting_urls = 1;
// The reporting URLs registered during the execution of reportResult() of the
// seller in case of single seller auction and component seller in case of
// multi seller auctions. These URLs will be pinged from the client.
ReportingUrls component_seller_reporting_urls = 2;
// The reporting URLs registered during the execution of reportResult() of the
// top level seller in case of multi seller auction. These URLs will be pinged
// from the client. This will not be set for single seller auctions.
ReportingUrls top_level_seller_reporting_urls = 3;
}
buyer_reporting_id for the winning buyer will be sent back to the client in the SelectAdResponse.
Reporting function execution is enabled by 2 flags on the server:
enableReportResultUrlGeneration
: To enable executing thereportResult()
functionenableReportWinUrlGeneration
: To enable executing thereportWin()
function.
If enableReportResultUrlGeneration
is set to false
, neither of the functions
are executed.
The code executed in the auction service is loaded only once at startup time, and periodically every few hours (as configured by the developer) in the Auction service from endpoints where the ad tech's reporting code modules are hosted.
The function name to be called is determined at run time before the dispatch request. The code is periodically fetched from public endpoints provided by the seller and buyer. The code loaded into Roma should to contain the following functions:
scoreAd()
reportResult()
reportWin()
for all buyers.reportWin()
is called only for the winner.
The reportWin()
functions are loaded if enableReportWinUrlGeneration
has
been set to true
.
Note: Try to keep reportWin()
in a separate code module from generateBid()
,
so that a seller can only fetch the partner buyers' reportWin()
code modules.
The Buyer reportWin()
UDF URL on the Auction Service will be validated as per the following criteria:
- The URL must have the same origin as the interest group owner for Chrome (specified for an IG by the buyer on device) and buyer origin for Android (specified for a custom audience by the buyer on device). For example, if the interest group owner is https://example.com/ab, the script must be loaded from https://example.com/foo/bar/foo/…
- If the interest group owner is https://ab.example.com, the script must be loaded from https://ab.example.com/foo/bar/foo/… In this case, B&A would reject any script from https://example.com/foo/bar/foo/…
- No query parameters or fragments are allowed: https://url.spec.whatwg.org/#concept-url-fragment. These will be removed from the configured reportWin URL while trying to fetch the script.
- The response from the
reportWin()
endpoint must contain the response headerX-Allow-Fledge:true
(similar to Chrome). - Redirects will be supported to HTTPS/HTTP URLs only.
If the udf endpoint fails the validation, the buyer reportWin()
function will not be loaded into Roma or executed on the Auction service. An error log will be printed. However the winner will still be returned to the client.
The reportResult function is called using a wrapper JavaScript function such as:
function reportResultEntryFunction(auctionConfig, sellerReportingSignals, directFromSellerSignals, enable_logging) {
…
const ps_report_result_response = {
signalsForWinner : "null",
reportResultUrl : "",
interactionReportingUrls : {},
}
…
return {
response: ps_report_result_response,
logs: ps_logs,
errors: ps_errors,
warnings: ps_warns
}
}
The reportWin function is called using a wrapper JavaScript function such as:
function reportWinEntryFunction(auctionSignals, perBuyerSignals, signalsForWinner, buyerReportingSignals, directFromSellerSignals, enable_logging) {
…
const ps_report_win_response = {
reportWinUrl: '',
interactionReportingUrls: {},
sendReportToInvoked: false,
registerAdBeaconInvoked: false,
};
…
reportWin(
auctionSignals, perBuyerSignals, signalsForWinner,
buyerReportingSignals, directFromSellerSignals);
…
return {
response: ps_report_win_response,
buyerLogs: ps_logs,
buyerErrors: ps_errors,
buyerWarnings: ps_warns
};
}
During each function call, the ad tech can log information or error messages using console.log
,console.warn
and console.error
functions. These logs are exported in the response from the wrapper. Log capture is enabled only if user-consented debugging is enabled.
During each function call, the ad tech can log information or error messages
using console.log
and console.error
functions. These logs are exported in
the response from the wrapper. Log capture is enabled only if the server side
flag enableAdTechCodeLogging
is set to true
. In the future, the logging will
be controlled by an additional user-consented debugging flag.
Chrome and Android will extract the reporting URLs and beacon URL map from the ProtectedAudience (encrypted AuctionResult) response. Upon rendering of the ad, reporting URLs are pinged based on the client controlled flag. Clients are expected to do an additional check to verify if the domain of the reporting URLs belong to the seller's or buyer's domain. The buyer_reporting_id returned in encrypted AuctionResult will be validated by the client with the buyerReportingId present as a part of the interest group attributes stored on-device. If the ids don’t match, the win reporting urls will be dropped.