FLEDGE API developer guide
Who is this article for?
This post is a technical reference to the current iteration of the experimental FLEDGE API.
The FLEDGE API is a less technical overview of the proposal, and also has a glossary.
The FLEDGE demo provides a walkthrough of a basic FLEDGE deployment.
The FLEDGE demo video explains how the demo code works, and shows how to use Chrome DevTools for FLEDGE debugging.
What is FLEDGE?
FLEDGE is a Privacy Sandbox proposal to serve remarketing and custom audience use cases, designed so that it cannot be used by third parties to track user browsing behavior across sites. The API enables on-device auctions by the browser, to choose relevant ads for websites the user has previously visited.
FLEDGE is the first experiment to be implemented in Chromium within the TURTLEDOVE family of proposals.
The diagram below provides an overview of the FLEDGE lifecycle: view a larger version.
How can I try FLEDGE?
FLEDGE demo
A walkthrough of a basic FLEDGE deployment across advertiser and publisher sites is available at fledge-demo.glitch.me.
The demo video explains how the demo code works, and shows how to use Chrome DevTools for FLEDGE debugging.
Take part in a FLEDGE origin trial
A Privacy Sandbox Relevance and Measurement origin trial has been made available in Chrome Beta 101.0.4951.26 and above on desktop for the FLEDGE, Topics, and Attribution Reporting APIs.
To take part, register for an origin trial token.
Once you have successfully enrolled in the trial, you can try out the FLEDGE JavaScript API on pages that provide a valid trial token: for example, to ask the browser to join one or more interest groups, and then to run an ad auction to select and display an ad.
The FLEDGE demo provides a basic example of an end-to-end FLEDGE deployment.
Provide a trial token for every page on which you would like to run FLEDGE API code:
As a meta tag in the <head>:
<meta http-equiv="origin-trial" content="TOKEN_GOES_HERE">
As an HTTP header:
Origin-Trial: TOKEN_GOES_HERE
By providing a token programmatically:
const otMeta = document.createElement('meta');
otMeta.httpEquiv = 'origin-trial';
otMeta.content = 'TOKEN_GOES_HERE';
document.head.append(otMeta);
An iframe running FLEDGE code—such as a navigator.joinAdInterestGroup()
call by an interest group owner—will need to provide a token that matches its origin.
Proposed First FLEDGE Origin Trial Details provides more details about the goals of the first trial and explains what features are supported.
Not all users may be eligible for the Privacy Sandbox Relevance and Measurement origin trial, even on pages that provide a valid trial token.
Testing the Privacy Sandbox ads relevance and measurement APIs explains why this is so, and shows how you can (and should) detect if an origin trial feature is available before attempting to use it.
chrome://flags
or feature flags
Test with You can test FLEDGE for a single user in Chrome Beta 101.0.4951.26 and above on desktop:
- By enabling
chrome://flags/#privacy-sandbox-ads-apis
. - By setting flags from the command line.
Render ads in iframes or fenced frames
Ads can be rendered in an <iframe>
or a <fencedframe>
, depending on which flags are set.
To use <fencedframe>
to render ads:
--enable-features=InterestGroupStorage,AdInterestGroupAPI,Fledge,FencedFrames
To use <iframe>
to render ads:
--enable-features=InterestGroupStorage,AdInterestGroupAPI,Fledge,AllowURNsInIframes --disable-features=FencedFrames
Include the BiddingAndScoringDebugReportingAPI
flag to enable the temporary debug loss/win reporting methods.
Run Chromium with flags explains how to set flags when running Chrome and other Chromium-based browsers from the command line. The full list of FLEDGE flags is available from Chromium Code Search.
This is an in-progress version of FLEDGE for early testing. It shouldn't be considered complete or indicative of the final implementation. FLEDGE progress and status are discussed in the regular WICG meetings.
The minutes for the 2021-05-12 WICG call provide detail on what is and is not supported in the current implementation.
The Privacy Sandbox timeline provides implementation timing information for FLEDGE and other Privacy Sandbox proposals.
What features are supported in the latest version of Chrome?
FLEDGE is being made available behind feature flags in Chromium as a first experiment to test the following features of the FLEDGE proposal:
- Interest groups: stored by the browser, with associated metadata to configure ad bidding and rendering.
- On-device bidding by buyers (DSP or advertiser): based on stored interest groups and signals from the seller.
- On-device ad selection by the seller (SSP or publisher): based on auction bids and metadata from buyers.
- Ad rendering in a temporarily relaxed version of Fenced Frames: with network access and logging allowed for ad rendering.
The API explainer provides more detail about feature support and constraints.
Interest group permissions
The default in the current implementation of FLEDGE is to allow calling joinAdInterestGroup()
from anywhere in a page, even from cross-domain iframes. In the future, once site owners have had time to adjust their cross-domain iframe permissions policies, the plan is to disallow calls from cross-domain iframes, as the explainer describes.
Key/Value service
As part of a FLEDGE ad auction, the browser can access a key/value service that returns simple key-value pairs to provide information to an ad buyer, such as remaining campaign budget. The FLEDGE proposal mandates that this server "performs no event-level logging and has no other side effects based on these requests".
The FLEDGE Key/Value service code is now available in a Privacy Sandbox GitHub repository. This service can be used by Chrome and Android developers. Check out the announcement blog post for the status update. Learn more about the FLEDGE Key/Value service from the API explainer and the trust model explainer.
For initial testing, "Bring Your Own Server" model is used. In the long-term, adtechs will need to use the open-source FLEDGE Key/Value services running in trusted execution environments for retrieving real-time data.
To ensure that the ecosystem has sufficient time to test, we don’t expect to require the use of the open-source Key/Value services or TEEs until sometime after third-party cookie deprecation. We will provide substantial notice for developers to begin testing and adoption before this transition takes place.
Detect feature support
Before using the API, check if it's supported by the browser and available in the document:
'joinAdInterestGroup' in navigator &&
document.featurePolicy.allowsFeature('join-ad-interest-group') &&
document.featurePolicy.allowsFeature('run-ad-auction') ?
console.log('navigator.joinAdInterestGroup() is supported on this page') :
console.log('navigator.joinAdInterestGroup() is not supported on this page');
Feature support on the current page isn't a guarantee that an API is usable: the user may have disabled the API via browser settings, or they may have other settings that prevent the API from being used. In order to protect user privacy, there is no way to check for this programmatically.
How can I opt out of FLEDGE?
You can block access to the FLEDGE API either as a site owner, or as an individual user.
How can sites control access?
FLEDGE will eventually require sites to set a Permissions Policy to allow FLEDGE functionality to be available. This will help ensure that arbitrary third parties can't use the API without a site's knowledge. However, to facilitate testing during the first origin trial, this requirement is waived by default. Sites that would like to explicitly disable FLEDGE functionality during the testing period can use the relevant Permissions Policy to block access.
There are two FLEDGE permissions policies that can be set independently:
join-ad-interest-group
enables/disables functionality to add a browser to interest groupsrun-ad-auction
enables/disables functionality to run an on-device auction
Access to FLEDGE APIs can be disabled completely in first-party contexts by specifying the following permissions policy in an HTTP response header:
Permissions-Policy: join-ad-interest-group=(), run-ad-auction=()
You can disable usage of the APIs in an iframe by adding the following allow
attribute to an iframe element:
<iframe src="https://example.com" allow="join-ad-interest-group 'none'; run-ad-auction 'none'"></iframe>
The Proposed First FLEDGE Origin Trial Permissions-Policy section provides more detail.
User opt-out
A user can block access to the FLEDGE API and other Privacy Sandbox features by using any of the following mechanisms:
- Disable the Privacy Sandbox trials in Chrome Settings: Settings > Security and privacy > Privacy Sandbox. This is also accessible at
chrome://settings/privacySandbox
. - Disable third-party cookies in Chrome Settings: Settings > Security and privacy.
- Set Cookies and other site data to either "Block third-party cookies" or "Block all cookies" from
chrome://settings/cookies
. - Use Incognito mode.
The FLEDGE explainer provides more detail about API design elements and describes how the API seeks to meet privacy goals.
Debug FLEDGE worklets
From Chrome Canary 98.0.4718.0, it's possible to debug FLEDGE worklets within Chrome DevTools.
The first step is to set breakpoints via a new category in the Event Listener Breakpoints pane in the Sources panel.
When a breakpoint triggers, execution is paused before the first statement at the top-level of the worklet script. You can use regular breakpoints or step commands to get to the bidding/scoring/reporting function itself.
Live worklet scripts will also show up under the Threads panel.
Since some worklets may run in parallel, multiple threads may end up in the "paused" state there; you can use the thread list to switch between threads, and resume or inspect them more closely as appropriate.
Observe FLEDGE events
From the Application panel in Chrome DevTools, you can observe FLEDGE interest group and auction events.
If you visit the FLEDGE demo shopping site in a browser with FLEDGE enabled, DevTools will display information about the join
event.
Now, if you visit the FLEDGE demo publisher site in a browser with FLEDGE enabled, DevTools displays information about the bid
and win
events.
You'll need to refresh the page to see FLEDGE events if DevTools wasn't open when you navigated to the site.
How does the FLEDGE API work?
In this example, a user browses the website of a custom bike maker, then later visits a news website and is shown an ad for a new bike from the bike maker.
Not all features described in this post have been implemented (or fully implemented) in the version of the FLEDGE API currently being tested in Chrome. Test with feature flags explains what FLEDGE features are currently available for testing in Chrome run from the command line using feature flags.
We expect the features of FLEDGE will be added over time as work on implementation continues. Once the API reaches the origin trial stage, we'll provide a regularly-updated list of which parts are already implemented and what's still in progress.
1. A user visits an advertiser site
Imagine that a user visits the website of a custom bike maker (the advertiser in this example) and spends some time on the product page for a handmade steel bike. This provides the bike maker with a remarketing opportunity.
A demand-side platform (DSP) is an adtech service used to automate ad purchasing. DSPs are used by advertisers to buy ad impressions across a range of publisher sites. Publishers put their ad inventory up for sale through marketplaces called ad exchanges, and DSPs decide programmatically which available ad impression makes most sense for an advertiser to buy.
A supply-side platform (SSP) is an adtech service used to automate selling ad inventory. SSPs allow publishers to offer their inventory (empty rectangles where ads will go) to multiple ad exchanges, DSPs, and networks. This enables a wide range of potential buyers to bid for ad space.
2. The user's browser is asked to add an interest group
Explainer section: Browsers Record Interest Groups
The advertiser's demand-side platform (DSP) (or the advertiser itself) calls navigator.joinAdInterestGroup()
to ask the browser to add an interest group to the list of groups the browser is a member of. In this example, the group is named custom-bikes
, and the owner is dsp.example
. The interest group owner (in this case, the DSP) will be a buyer in the ad auction described in step 4. Interest group membership is stored by the browser, on the user's device, and is not shared with the browser vendor or anyone else.
The origin of the calling context for joinAdInterestGroup()
must match the interest group owner's origin, so joinAdInterestGroup()
will need to be called from an iframe (for example, from a DSP) unless the origin of the interest group owner matches the origin of the current document (for example, a website with its own interest groups).
runAdAuction
doesn't have the same requirements, so calling runAdAuction()
from a <script> tag is probably far more performant than a cross-origin iframe.
joinAdInterestGroup()
requires permission from:
- The site being visited
- The interest group owner
For example: it must not be possible for malicious.example
to call joinAdInterestGroup()
with dsp.example
as owner without the permission of dsp.example
.
Permission from the site being visited
Same origin: By default, permission is implicitly granted for joinAdInterestGroup()
calls from the same origin as the site being visited, i.e. from the same origin as the top-level frame of the current page. Sites can use a FLEDGE permissions policy header join-ad-interest-group
directive to disable joinAdInterestGroup()
calls.
Cross origin: Calling joinAdInterestGroup()
from origins that are different from the current page can only succeed if the site being visited has set a permissions policy that allows calls to joinAdInterestGroup()
from cross-origin iframes.
The default in the current implementation of FLEDGE is to allow calls to joinAdInterestGroup()
from anywhere in a page, even from cross-origin iframes. In the future, once site owners have had time to adjust their permissions policies, the plan is by default to disallow calls from cross-origin iframes, as described in the FLEDGE explainer.
Permission from the interest group owner
Interest group owner permission is implicitly granted by calling joinAdInterestGroup()
from an iframe with the same origin as that of the interest group's owner. For example, a dsp.example
iframe can call joinAdInterestGroup()
for interest groups owned by dsp.example
.
The proposal is that joinAdInterestGroup()
can run in a page or iframe in the owner's domain, or be delegated to other domains provided using a list at a .well-known
URL.
Using navigator.joinAdInterestGroup()
Here's an example of how the API might be used:
const interestGroup = {
owner: 'https://dsp.example',
name: 'custom-bikes',
biddingLogicUrl: ...,
biddingWasmHelperUrl: ...,
dailyUpdateUrl: ...,
trustedBiddingSignalsUrl: ...,
trustedBiddingSignalsKeys: ['key1', 'key2'],
userBiddingSignals: {...},
ads: [bikeAd1, bikeAd2, bikeAd3],
adComponents: [customBike1, customBike2, bikePedal, bikeFrame1, bikeFrame2],
};
navigator.joinAdInterestGroup(interestGroup, 7 * kSecsPerDay);
The interestGroup
object passed to the function must be no more than 50 kiB in size, otherwise the call will fail. The second parameter specifies the duration of the interest group, capped at 30 days. Successive calls overwrite previously stored values.
All URLs used as parameters for FLEDGE API methods must be from secure origins: all resources must be served over HTTPS URLs. How to use HTTPS for local development explains how to do this when running FLEDGE locally.
In addition, biddingLogicUrl
, decisionLogicUrl
, and trustedBiddingSignals
all require an X-Allow-FLEDGE: true
HTTP response header.
Interest group properties
Property | Required | Example | Role |
---|---|---|---|
owner | Required | 'https://dsp.example' | Origin of the interest group owner. |
name | Required | 'custom-bikes' | Name of the interest group. |
biddingLogicUrl ** | Optional* | 'https://dsp.example/bid/custom-bikes/bid.js' | URL for bidding JavaScript run in worklet. |
biddingWasmHelperUrl ** | Optional* | 'https://dsp.example/bid/custom-bikes/bid.wasm' | URL for WebAssembly code driven from biddingLogicUrl . |
dailyUpdateUrl ** | Optional | 'https://dsp.example/bid/custom-bikes/update' | URL that returns JSON to update interest group attributes. (See Update the interest group.) |
trustedBiddingSignalsUrl ** | Optional | 'https://dsp.example/trusted/bidding-signals' | Base URL for key-value requests to bidder's trusted server. |
trustedBiddingSignalsKeys | Optional | ['key1', 'key2' ...] | Keys for requests to key-value trusted server. |
userBiddingSignals | Optional | {...} | Additional metadata the owner can use during bidding. |
ads | Optional* | [bikeAd1, bikeAd2, bikeAd3] | Ads that might be rendered for this interest group. |
adComponents | Optional | [customBike1, customBike2, bikePedal, bikeFrame1, bikeFrame2] | Components for ads composed of multiple pieces. |
* All properties are optional except for owner
and name
. The biddingLogicUrl
and ads
properties are optional, but required to participate in an auction. There may be use cases for creating an interest group without these properties: for example, an interest group owner might want to add a browser to an interest group for a campaign that isn't running yet, or for some other future use, or they may temporarily have run out of advertising budget.
** In the current implementation of FLEDGE, biddingLogicUrl
, biddingWasmHelperUrl
, dailyUpdateUrl
and trustedBiddingSignalsUrl
must have the same origin as owner. That may not be a long-term constraint, and the ads
and adComponents
URLs have no such constraint.
Update interest group attributes
dailyUpdateUrl
specifies a web server that returns JSON defining interest group properties, corresponding to the interest group object passed to navigator.joinAdInterestGroup()
. This provides a mechanism for the group's owner to periodically update the attributes of the interest group. In the current implementation, the following attributes can be changed:
biddingLogicUrl
biddingWasmHelperUrl
trustedBiddingSignalsUrl
trustedBiddingSignalsKeys
ads
priority
Any field not specified in the JSON will not be overwritten—only fields specified in the JSON get updated—whereas calling navigator.joinAdInterestGroup()
overwrites any existing interest group.
Updates are best-effort, and can fail under the following conditions:
- Network request timeout (currently 30 seconds).
- Other network failure.
- JSON parsing failure.
Updates can also be canceled if too much contiguous time has been spent updating, though this doesn't impose any rate limiting on canceled (remaining) updates. Updates are rate-limited to a maximum of one per day. Updates that fail due to network errors are retried after an hour, and updates that fail due to disconnection from the internet are retried immediately on reconnection.
Manual updates
Updates to interest groups owned by the current frame's origin can be triggered manually via navigator.updateAdInterestGroups()
. Rate limiting prevents updates from happening too frequently: repeated calls to navigator.updateAdInterestGroups()
don't do anything until the rate limit period (currently one day) has passed. The rate limit gets reset if navigator.joinAdInterestGroup()
is called again for the same interest group owner
and name
.
Automatic updates
All interest groups loaded for an auction are updated automatically after an auction completes, subject to the same rate limits as manual updates. For each owner with at least one interest group participating in an auction, it's as if navigator.updateAdInterestGroups()
is called from an iframe whose origin matches that owner.
Specify ads for an interest group
ads
and adComponents
objects include a URL for an ad creative and, optionally, arbitrary metadata that can be used at bidding time. For example:
{
renderUrl: 'https://cdn.example/.../bikeAd1.html',
metadata: bikeAd1metadata // optional
}
How do buyers make bids?
The script at biddingLogicUrl
provided by an interest group owner must include a generateBid()
function. When an ad-space seller calls navigator.runAdAuction()
, the generatedBid()
function is called once for each of the interest groups the browser is a member of, if the interest group's owner is invited to bid. In other words, generateBid()
is called once for each candidate ad. The seller provides a decisionLogicUrl
property on the auction configuration parameter passed to navigator.runAdAuction()
. The code at this URL must include a scoreAd()
function, which is run for each bidder in the auction, to score each of the bids returned by generateBid()
.
The biddingWasmHelperUrl
property is optional, but it allows the bidder to provide computationally-expensive subroutines in WebAssembly, rather than JavaScript, to be driven from the JavaScript function provided by biddingLogicUrl
. If provided, it must point to a WebAssembly binary, delivered with an application/wasm mimetype
. The corresponding WebAssembly.Module
is made available by the browser to the generateBid()
function.
The script at biddingLogicUrl
provided by an ad-space buyer must include a generateBid()
function. This function is called once for each candidate ad. runAdAuction()
individually checks each ad, along with its associated bid and metadata, then assigns the ad a numerical desirability score.
generateBid(interestGroup, auctionSignals, perBuyerSignals,
trustedBiddingSignals, browserSignals) {
...
return {
ad: adObject,
bid: bidValue,
render: renderUrl,
adComponents: [adComponentRenderUrl1, ...]
};
}
generateBid()
takes the following arguments:
interestGroup
The object passed tojoinAdInterestGroup()
by the ad buyer. (The interest group may be updated viadailyUpdateUrl
.)auctionSignals
A property of the auction config argument passed tonavigator.runAdAuction()
by the ad-space seller. This provides information about page context (such as the ad size and the publisher ID), the type of auction (first-price or second-price), and other metadata.perBuyerSignals
As withauctionSignals
, a property of the auction configuration argument passed tonavigator.runAdAuction()
by the seller. This can provide contextual signals from the buyer's server about the page, if the seller is an SSP which performs a real-time bidding call to buyer servers and pipes the response back, or if the publisher page contacts the buyer's server directly. If so, the buyer may wish to check a cryptographic signature of those signals inside generateBid() as protection against tampering.trustedBiddingSignals
An object whose keys are thetrustedBiddingSignalsKeys
for the interest group, and whose values are returned in thetrustedBiddingSignals
request.browserSignals
An object constructed by the browser, which might include information about page context (such as thehostname
of the current page, which the seller could otherwise fake) and data for the interest group itself (such as a record of when the group previously won an auction, to allow on-device frequency capping).
The browserSignals
object has the following properties:
{
topWindowHostname: 'publisher.example',
seller: 'https://ssp.example',
joinCount: 3,
bidCount: 17,
prevWins: [[time1,ad1],[time2,ad2],...],
wasmHelper: ... /* WebAssembly.Module object based on interest group's biddingWasmHelperUrl. */
dataVersion: 1, /* Data-Version value from the buyer's Key/Value service response(s). */
}
To calculate a bid
value, code in generateBid()
can use the properties of the function's parameters. For example:
function generateBid(interestGroup, auctionSignals, perBuyerSignals,
trustedBiddingSignals, browserSignals) {
return {
...
bid: auctionSignals.is_above_the_fold ? perBuyerSignals.atf_value : perBuyerSignals.btf_value,
...
}
}
generateBid()
returns an object with four properties:
ad
Arbitrary metadata about the ad, such as information the seller expects to learn about this bid or ad creative. The seller uses this information in its auction and decision logic.bid
A numerical bid that will enter the auction. The seller must be in a position to compare bids from different buyers, therefore bids must be in some seller-chosen unit (e.g. "USD per thousand"). If the bid is zero or negative, then this interest group will not participate in the seller's auction at all. With this mechanism, the buyer can implement any advertiser rules for where their ads may or may not appear.render
A URL, or a list of URLs, that will be used to render the creative if this bid wins the auction. (See Ads Composed of Multiple Pieces in the API explainer.) The value has to match therenderUrl
of one of the ads defined for the interest group.adComponents
An optional list of up to 20 components for ads composed of multiple pieces, taken from theadComponents
property of the interest group argument passed tonavigator.joinAdInterestGroup()
.
Asking a browser to leave an interest group
The interest group owner can request that a browser be removed from an interest group. In other words, the browser is asked to remove the interest group from the list of those it is a member of.
navigator.leaveAdInterestGroup({
owner: 'https://dsp.example',
name: 'custom-bikes'
});
If a user returns to the site which asked the browser to add an interest group, the interest group owner can call the navigator.leaveAdInterestGroup()
function to request the browser remove the interest group. Code for an ad can also call this function for its interest group.
3. The user visits a site that sells ad space
Later, the user visits a site that sells ads space, in this example a news website. The site has ad inventory, which it sells programmatically using real-time bidding.
There are three main roles described in the FLEDGE proposal explainer:
- Advertiser: a site that pays to advertise its products. In the example here, a custom bike maker.
- Publisher: sites that sell ad space, such as the online news website referred to in the examples here. Many (but not all) sites selling ad space are content publishers.
- Seller: the party running the ad auction (in the next step). Most publishers use an adtech service such as an SSP to optimize selling ad inventory.
4. An ad auction is run in the browser
Explainer section: Sellers Run On-Device Auctions
The ad auction is likely to be run by the publisher's SSP, or the publisher itself. The purpose of the auction is to select the most appropriate ad for a single available ad slot on the current page. The auction takes into account the interest groups the browser is a member of, along with data from ad-space buyers and the sellers from the Key/Value services.
The ad-space seller makes a request to the user's browser to begin an ad auction by calling navigator.runAdAuction()
.
For example:
const auctionConfig = {
seller: 'https://ssp.example',
decisionLogicUrl: ...,
trustedScoringSignalsUrl: ...,
interestGroupBuyers: ['https://dsp.example', 'https://buyer2.example', ...],
auctionSignals: {...},
sellerSignals: {...},
sellerTimeout: 100,
perBuyerSignals: {
'https://dsp.example': {...},
'https://another-buyer.example': {...},
...
},
perBuyerTimeouts: {
'https://dsp.example': 50,
'https://another-buyer.example': 200,
'*': 150,
...
},
componentAuctions: [
{
'seller': 'https://some-other-ssp.example',
'decisionLogicUrl': ...,
...
},
...
]
};
const auctionResultPromise = navigator.runAdAuction(auctionConfig);
runAdAuction()
returns a promise that resolves to a URN (urn:uuid:<something>
) that represents the ad auction outcome. This can only be decoded by the browser when passed to a fenced frame for rendering: the publisher page cannot inspect the winning ad.
As explained earlier, the origin of the calling context for joinAdInterestGroup()
must match the interest group owner's origin, so joinAdInterestGroup()
will need to be called from an iframe (for example, from a DSP) unless the origin of the interest group owner matches the origin of the current document (for example, a website with its own interest groups).
runAdAuction
doesn't have the same requirements, so calling runAdAuction()
from a <script> tag is probably far more performant than a cross-origin iframe.
The decisionLogicUrl
script considers each individual ad, along with its associated bid and metadata, one at a time, and then assigns it a numerical desirability score.
auctionConfig
properties
Property | Required | Example | Role |
---|---|---|---|
seller | Required | 'https://ssp.example' | Origin of the seller. |
decisionLogicUrl | Required | 'https://ssp.example/auction-decision-logic.js' | URL for auction worklet JavaScript. |
trustedScoringSignalsUrl | Optional | 'https://ssp.example/scoring-signals' | URL of seller's trusted server. |
interestGroupBuyers* | Required | ['https://dsp.example', 'https://buyer2.example', ...] | Origins of all interest group owners asked to bid in the auction. |
auctionSignals | Optional | {...} | Seller information about page context, type of auction, etc. |
sellerSignals | Optional | {...} | Information based on publisher settings, making a contextual ad request, etc. |
sellerTimeout | Optional | 100 | Maximum runtime (ms) of seller's scoreAd() script. |
perBuyerSignals | Optional | {'https://dsp.example': {...}, | Contextual signals about the page for each specific buyer, from their server. |
perBuyerTimeouts | Optional | 50 | Maximum runtime (ms) of particular buyer's generateBid() scripts. |
componentAuctions | Optional | [{'seller': 'https://www.some-other-ssp.com', | Additional configurations for component auctions. |
* The seller may specify interestGroupBuyers: '*'
to permit all interest groups to bid. Ads are then accepted or rejected based on criteria other than inclusion of the interest group owner. For example, the seller may review ad creatives to confirm compliance with their policies.
** additionalBids
is not supported in the current implementation of FLEDGE. Read the Auction Participants section in the FLEDGE explainer for more information.
How are ads selected?
The code at decisionLogicUrl
(a property of the auction configuration object passed to runAdAuction()
) must include a scoreAd()
function. This is run once for each ad to determine its desirability.
scoreAd(adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) {
...
return desirabilityScoreForThisAd;
}
scoreAd()
takes the following arguments:
adMetadata
Arbitrary metadata provided by the buyer.bid
A numerical bid value.auctionConfig
The auction configuration object passed tonavigator.runAdAuction()
.trustedScoringSignals
Values retrieved at auction time from the seller's trusted server, representing the seller's opinion of the ad.browserSignals
An object constructed by the browser, including information that the browser knows and which the seller's auction script might want to verify:
{
topWindowHostname: 'publisher.example',
interestGroupOwner: 'https://dsp.example',
renderUrl: 'https://cdn.example/render',
adComponents: ['https://cdn.com/ad-component-1', ...],
biddingDurationMsec: 12,
dataVersion: 1 /* Data-Version value from the seller's Key/Value service response. */
}
Before an auction starts, the seller finds the best contextual ad for the available ad slot. Part of its scoreAd()
logic is to reject any ad that can't beat the contextual winner.
5. The seller and participating buyers receive realtime data from the Key/Value service
Explainer section: Fetching Real-Time Data from the FLEDGE Key/Value service.
During an ad auction, the ad-space seller can get realtime data about specific ad creatives by making a request to a Key/Value service using the trustedScoringSignalsUrl
property of auction configuration argument passed to navigator.runAdAuction()
, along with the keys from the renderUrl
properties of all entries in the ads
and adComponents
fields of all interest groups in the auction.
Likewise, an ad-space buyer can request realtime data from the Key/Value service using the trustedBiddingSignalsUrl
and trustedBiddingSignalsKeys
properties of the interest group argument passed to navigator.joinAdInterestGroup()
.
When runAdAuction()
is called, the browser makes a request to each ad buyer's trusted server. The URL for the request might look like this:
https://kv-service.example/getvalues?hostname=publisher.example&keys=key1,key2
- The base URL comes from
trustedBiddingSignalsUrl
. - The
hostname
is provided by the browser. - The
keys
value is taken fromtrustedBiddingSignalsKeys
.
The response to this request is a JSON object providing values for each of the keys.
In the current initial experimental phase for testing FLEDGE, trustedBiddingSignalsUrl
must have the same origin as the interest group owner: see Interest group properties and Bring Your Own Server.
6. The winning ad is displayed
Explainer section: Browsers Render the Winning Ad
As described earlier: the promise returned by runAdAuction()
resolves to an URN which is passed to a fenced frame for rendering, and the site displays the winning ad.
7. The auction result is reported
Explainer section: Event-Level Reporting (for now)
The long-term plan is to enable the browser to report auction results for the seller and buyers using aggregate reporting APIs. As a temporary event-level reporting mechanism, the code implementing reportResult()
for the seller, and reportWin()
for the winning bidder, can call the sendReportTo()
function. This takes a single argument: a string representing a URL that is fetched after the auction completes, which encodes event-level information to be reported.
Seller reports outcome
Explainer section: Seller Reporting on Render
The seller's JavaScript provided at decisionLogicUrl
(which also provided scoreAd()
) can include a reportResult()
function, to report the auction outcome.
reportResult(auctionConfig, browserSignals) {
...
return signalsForWinner;
}
The arguments passed to this function are:
auctionConfig
The auction configuration object passed tonavigator.runAdAuction()
.browserSignals
An object constructed by the browser providing information about the auction. For example:{
'topWindowHostname': 'publisher.example',
'interestGroupOwner': 'https://dsp.example',
'renderUrl': 'https://cdn.example/url-of-winning-creative.wbn',
'bid:' <bidValue>,
'desirability': <winningAdScore>
}
The return value of this function is used as the sellerSignals
argument for the winning bidder's reportWin()
function.
Winning bidder reports outcome
Explainer section: Buyer Reporting on Render and Ad Events
The winning bidder's JavaScript (which also provided generateBid()
) can include a reportWin()
function to report the auction outcome.
reportWin(auctionSignals, perBuyerSignals, sellerSignals, browserSignals) {
...
}
The current implementation of FLEDGE in Chrome will warn if reportWin()
is not defined.
The arguments passed to this function are:
auctionSignals
andperBuyerSignals
The same values passed togenerateBid()
for the winning bidder.sellerSignals
The return value ofreportResult()
, which gives the seller an opportunity to pass information to the buyer.browserSignals
An object constructed by the browser providing information about the auction. For example:{
'topWindowHostname': 'publisher.example',
'seller': 'https://ssp.example',
'interestGroupOwner': 'https://dsp.example',
'interestGroupName': 'custom-bikes',
'renderUrl': 'https://cdn.example/winning-creative.wbn',
'bid:' <bidValue>
}
Temporary loss/win reporting implementation
There are two methods available temporarily in Chrome for auction reporting:
forDebuggingOnly.reportAdAuctionLoss()
forDebuggingOnly.reportAdAuctionWin()
These methods each take a single argument: a URL to fetch after the auction is completed. They can be called multiple times, in both scoreAd()
and generateBid()
, with different URL arguments.
Chrome only sends debug loss/win reports when an auction runs to completion. If an auction is canceled (for example, due to a new navigation) no reports will be generated.
These methods are available by default in Chrome if chrome://flags/#privacy-sandbox-ads-apis
is enabled. But, if you're running Chrome with command line flags to enable FLEDGE, you'll need to explicitly enable the methods by including the BiddingAndScoringDebugReportingAPI
flag. If the flag is not enabled, the methods will still be available but do nothing.
8. An ad click is reported
A click on an ad rendered in a fenced frame is reported. To learn more about how this might work, see Fenced Frames Ads Reporting.
The diagram below outlines each stage of a FLEDGE ad auction: view a larger version.
What is the difference between FLEDGE and TURTLEDOVE?
What is the difference between FLEDGE and TURTLEDOVE?
FLEDGE is the first experiment to be implemented in Chromium within the TURTLEDOVE family of proposals.
FLEDGE follows TURTLEDOVE's high-level principles. Some online advertising has been based on showing an ad to a potentially-interested person who has previously interacted with the advertiser or ad network. Historically this has worked by the advertiser recognizing a specific person as they browse across web sites, a core privacy concern with today's web.
The TURTLEDOVE effort is about offering a new API to address this use case while offering some key privacy advances:
- The browser, not the advertiser, holds the information about what the advertiser thinks a person is interested in.
- Advertisers can serve ads based on an interest, but cannot combine that interest with other information about a person — in particular, who they are or what page they are visiting.
FLEDGE grew out of TURTLEDOVE and a collection of related proposals for modifications to better served the developers who would be using the API:
- In SPARROW: Criteo proposed the addition of a ("Gatekeeper") service model running in a trusted execution environment (TEE). FLEDGE includes a more limited use of TEEs, for real-time data lookup and aggregated reporting.
- NextRoll's TERN and Magnite's PARRROT proposals described the different roles that buyers and sellers had in the on-device auction. FLEDGE's ad bidding/scoring flow is based on this work.
- RTB House's Outcome-based and Product-level TURTLEDOVE modifications improved the anonymity model and personalization capabilities of the on-device auction
- PARAKEET is Microsoft's proposal for a TURTLEDOVE-like ad service that relies on a proxy server running in a TEE between the browser and the adtech providers, to anonymize ad requests and enforce privacy properties. FLEDGE has not adopted this proxying model. We are bringing the JavaScript APIs for PARAKEET and FLEDGE into alignment, in support of future work to further combine the best features of both proposals.
FLEDGE does not yet prevent a website's ad network from learning which ads a person sees. We expect to modify the API to become more private over time.
What browser configuration is available?
What browser configuration is available?
Users can adjust their participation for Privacy Sandbox trials in Chrome by enabling or disabling the top-level setting in chrome://settings/privacySandbox. During initial testing, people will be able to use this high-level Privacy Sandbox setting to opt out of FLEDGE. Chrome plans to allow users to see and manage the list of interest groups that they have been added to across the web sites they have visited. As with the Privacy Sandbox technologies themselves, user settings may evolve with feedback from users, regulators and others.
We'll continue to update the available settings in Chrome as the FLEDGE proposal progresses, based on tests and feedback. In the future, we plan to offer more granular settings to manage FLEDGE and associated data.
API callers can't access group membership when users browse in Incognito mode, and membership is removed when users clear their site data.
Engage and share feedback
- GitHub: Read the proposal, raise questions and follow discussion.
- W3C: Discuss industry use cases in the Improving Web Advertising Business Group.
- Developer support: Ask questions and join discussions on the Privacy Sandbox Developer Support repo.
- FLEDGE mailing list: fledge-api-announce provides announcements and updates about the API.
- Join the scheduled calls for FLEDGE (every second week). Everyone is welcome to join—to participate, first make sure to join the WICG. You can actively participate or just listen in!
- Use the Privacy Sandbox feedback form to share feedback privately with the Chrome team outside of public forums.
Get support
To ask a question about your implementation, about the demo, or about the documentation:
- Open a new issue on the privacy-sandbox-dev-support repository. Make sure to select the issue template for FLEDGE.
- Raise an issue on the demo code repo on GitHub.
- For more general questions about how to meet your use cases with the API, file an issue on the proposal repository.
For bugs and issues with the implementation of the FLEDGE API in Chrome:
- View existing issues reported for the API.
- Raise a new issue at crbug.com/new.
Get updates
- To be notified of status changes in the API, join the mailing list for developers.
- To closely follow all ongoing discussions on the API, click the Watch button on the proposal page on GitHub. This requires you have or create a GitHub account.
- To get overall updates on the Privacy Sandbox, subscribe to the RSS feed Progress in the Privacy Sandbox.
Find out more
- The FLEDGE API: less technical overview of the proposal.
- FLEDGE demo: walkthrough of a basic FLEDGE deployment.
- The FLEDGE demo video: explains the demo code, and shows how to use Chrome DevTools for FLEDGE debugging.
- FLEDGE API technical explainer
- Digging into the Privacy Sandbox
- Intent to prototype
Photo by Ray Hennessy on Unsplash.