Support custom audience targeting with the Protected Audience API

Provide feedback

Recent updates

Protected Audience is in Beta and is available for testing on public devices!

With Protected Audience, you can:

  • Manage custom audiences based on prior user actions.
  • Initiate on-device auctions with single seller or waterfall mediation support.
  • Exercise impression reporting after ad selection.

To get started, read the Protected Audience developer guide. Your feedback is appreciated as we continue to develop Protected Audience.

Timeline

We will release new features in the coming months. The exact release dates will vary depending on the feature, and this table will be updated with links to documentation as it becomes available.

Feature Available in Developer Preview Available in Beta
Event-level reporting Q1 '23 Q3 '23
Waterfall mediation Q1 '23 Q4 '23
App install ads filtering Q2 '23 Q3 '23
Frequency cap filtering Q2 '23 Q3 '23
Pass contextual ads to ad selection workflow for filtering Q2 '23 Q3 '23
Interaction reporting Q2 '23 Q3 '23
Join custom audience delegation Q3 '23 Q4 '23
non-CPM billing Q3 '23 Q4 '23
Bidding and Auction services integration Q3 '23 Q4 '23
Debug reporting Q3 '23 Q4 '23
Attribution Reporting integration N/A Q4 '23
Open bidding mediation Q4 '23 Q4 '23
Currency management Q1 '24 Q2 '24
K-anon integration N/A Q2 '24
Aggregate reporting integration Q3 '24 Q4 '24

Overview

In mobile advertising, advertisers commonly need to serve ads to potentially-interested users based on how they have previously engaged with the advertiser's app. For example, the developer of SportingGoodsApp may want to advertise to users who have items left in the shopping cart, by showing ads to remind users to complete the purchase. The industry commonly describes this general idea with terms such as "remarketing" and "custom audience targeting".

Today, these use cases are typically implemented by sharing contextual information about how the ad is shown (such as the app name) and private information such as audience lists with ad tech platforms. Using this information, advertisers can select relevant ads on their servers.

The Protected Audience API on Android (formerly known as FLEDGE) encompasses the following APIs for ad tech platforms and advertisers to support common interaction-based use cases in ways that limit the sharing of both identifiers across apps and a user's app interaction information with third-parties:

  1. Custom Audience API: This is centered on the "custom audience" abstraction, which represents an advertiser-designated audience with common intentions. Audience information is stored on-device and can be associated with relevant candidate ads for the audience and arbitrary metadata, such as bidding signals. The information can be used to inform advertiser bids, ad filtering, and rendering.
  2. Ad Selection API: This provides a framework that orchestrates ad tech platforms' workflows that leverage on-device signals to determine a "winning" ad by considering candidate ads stored locally, and performing additional processing on candidate ads that an ad tech platform returns to the device.
Flow chart that shows the custom audience management and ad selection workflow in the Privacy Sandbox on Android.

At a high level, the integration works as follows:

  1. SportingGoodsApp wants to remind its users to purchase merchandise items left in their cart if they haven't completed the purchase within 2 days. SportingGoodsApp uses the Custom Audience API to add the user to the "products in cart" audience list. The platform manages and stores this audience list on the device, limiting the sharing with third-parties. SportingGoodsApp partners with an ad tech platform to show its ads to users in its audience list. The ad tech platform manages metadata for audience lists and provides candidate ads, which is made available to the ad selection workflow for consideration. The platform can be configured to periodically fetch updated audience-based ads in the background. This helps keep the list of audience-based candidate ads fresh and uncorrelated with requests sent to third-party ad servers during the ad opportunity (i.e. contextual ad request).

  2. When the user plays the FrisbeeGame on the same device, they may see an ad reminding them to complete the purchase of items left in SportingGoodsApp's shopping cart. This can be achieved by FrisbeeGame (with an integrated ads SDK) invoking the Ad Selection API to select a winning ad for the user based on any audience list they may be a part of (in this example, the "products in cart" custom audience created by SportingGoodsApp). The ad selection workflow can be set up to consider ads retrieved from ad tech platforms' servers, in addition to on-device ads associated with the custom audiences as well as other on-device signals. The workflow can also be customized by the ad tech platform and ads SDK with custom bidding and scoring logic to achieve appropriate advertising goals. This approach enables the user's interest or app interactions data to inform ads selection, while limiting the sharing of this data with third-parties.

  3. The ad-serving app or ad tech platform's SDK renders the selected ad.

  4. The platform facilitates the reporting of impressions and ad selection results. This reporting capability is complementary to the Attribution Reporting API. Ad tech platforms may customize based on their reporting needs.

Get access to Protected Audience on Android APIs

Ad tech platforms need to enroll to access the Protected Audience API. See Enroll for a Privacy Sandbox account for more information.

Custom audience management

Custom audience

A custom audience represents a group of users as determined by the advertiser with common intentions or interests. An app or SDK may use a custom audience to indicate a particular audience such as someone who has "left items in their shopping cart" or "completed the beginner levels" of a game. The platform maintains and stores audience information locally on the device and does not expose which custom audiences the user is in. Custom audiences are distinct from Chrome's Protected Audience interest groups, and they can't be shared across web and apps. This helps limit the sharing of user information.

An advertiser app or the integrated SDK may join or leave a custom audience based on, for example, user engagement in an app.

Custom audience metadata

Each custom audience contains the following metadata:

  • Owner: Package name of the owner app. This is implicitly set to the package name of the caller app.
  • Buyer: Buyer ad network which manages ads for this custom audience. Buyer also represents the party who may access the custom audience and fetch relevant ad information. The buyer is specified following the eTLD+1 format.
  • Name: An arbitrary name or identifier for the custom audience, such as a user that has "shopping cart abandoners". This attribute could be used as, for example, one of the targeting criteria in the advertiser’s ad campaigns, or a query string in the URL for fetching bidding code.
  • Activation time and expiration time: These fields define the time window when this custom audience will be effective. The platform uses this information to withdraw membership from a custom audience. Expiration time cannot exceed a maximum duration window to limit the life of a custom audience.
  • Daily update URL: The URL that the platform uses to fetch candidate ads and other metadata defined in the "User bidding signals" and "Trusted bidding signals" fields on a recurring basis. For more details, see the section on how to fetch candidate ads for custom audiences.
  • User bidding signals: Ad tech platform-specific signals for any bidding of remarketing ads. Examples of signals include: the user's coarse location, preferred locale, and so on.
  • Trusted bidding data: Ad tech platforms rely on real-time data to inform Ad retrieval and scoring. For instance, an Ad may run out of budget and needs to be stopped serving immediately. An Ad-tech can define a URL endpoint from where this real-time data can be fetched and the set of keys for which the real-time lookup needs to be performed. The server handling this request will be a trusted server managed by the ad tech platform.
  • Bidding logic URL: The URL that the platform uses to fetch bidding code from the demand side platform. The platform performs this step when an ad auction is initiated.
  • Ads: A list of candidate ads for the custom audience. This includes ad tech platform-specific ad metadata and a URL to render the ad. When an auction is initiated for the custom audience, this list of ad metadata will be considered. This list of ads will be refreshed using the daily update URL endpoint when feasible. Due to resource constraints on mobile devices, there's a limit on the number of ads that can be stored in a custom audience.

Custom audience delegation

Traditional custom audience definition and configuration usually relies on server-side technologies and integrations driven by ad techs in partnership with agency and advertiser clients and partners. The Protected Audience API enables custom audience definition and configuration while protecting user privacy. To join a custom audience, Buyer ad techs that don't have an SDK presence in apps need to collaborate with ad techs who have an on-device presence, such as mobile measurement partners (MMPs) and supply-side platforms (SSPs). The Protected Audience API aim to support these SDKs by providing flexible solutions for custom audience management by enabling on device callers to invoke custom audience creation on behalf of buyers. This process is called custom audience delegation. Configure custom audience delegation by following these steps:

Join a custom audience

Joining a custom audience can be done in two ways:

joinCustomAudience()

An app or SDK can request to join a custom audience by calling joinCustomAudience() after instantiating the CustomAudience object with the expected parameters. Here is an illustrative code snippet example:

CustomAudience audience = new CustomAudience(
    Buyer = "example-dsp.com",
    Name = "running-shoes",
    ActivationTime = now(),
    ExpirationTime = ActivationTime.plus(30 days),
    DailyUpdateURL = Uri.parse("https://..."),
    UserBiddingSignals = new JSONObject("{...}"),
    TrustedBiddingURL = Uri.parse("https://..."),
    TrustedBiddingKeys = {'key1","key2", ...,"key n"},
    BiddingLogicURL =  Uri.parse("https://..."),
    Ads = [new AdData(renderUrl = Uri.parse("https://..."),
           metadata = new JSONObject("{...}"), ...];

// Invoke ad services API to join a custom audience.
joinCustomAudience(audience);

fetchAndJoinCustomAudience()

An app or SDK can request to join a custom audience on behalf of an ad tech who does not have an on-device presence by calling fetchAndJoinCustomAudience() with the expected parameters, as in the following example:

FetchAndJoinCustomAudienceRequest fetchRequest = new FetchAndJoinCustomAudienceRequest(
    // Example: Optional verification token passed inside the fetch URL
    FetchURI = Uri.parse("https://example-dsp.com/...?mytoken=arbitrary1234"),
    // All the following parameters are optional
    Name = "running-shoes",
    ActivationTime = now(),
    ExpirationTime = ActivationTime.plus(30 days),
    UserBiddingSignals = new JSONObject("{...}")
);

fetchAndJoinCustomAudience(fetchRequest);

The URL endpoint, owned by the buyer, responds back with a CustomAudience JSON object in the response body. Buyer and owner fields of the custom audience are ignored (and are autofilled by the API). The API also enforces that the daily update URL will also match the buyer's eTLD+1.

{
 "name": "running-shoes",
 "activation_time": 1680603133000,
 "expiration_time": 1680803133000,
 "user_bidding_signals" : {"signal1": "value"},
 "trusted_bidding_data": {
    "trusted_bidding_uri": "https://example-dsp.com/.."
    "trusted_bidding_keys": ["k1", "k2"],
 },
 "bidding_logic_uri": "https://example-dsp.com/...",
 "daily_update_uri": "https://example-dsp.com/...",
 "ads": [
   {
     "render_uri": "https://example-dsp.com/...",
     "metadata": {},
     "ad_filters": {
       "frequency_cap": {
         "win": [
           {
             "ad_counter_key": 1,
             "max_count": 2,
             "interval_in_seconds": 60
           },
         ],
         "view": [
           {
             "ad_counter_key": 2,
             "max_count": 10,
             "interval_in_seconds": 3600
           },
         ]
       },
       "app_install": {
         "package_names": [
           "package.name.one",
           "package.name.two", ...
         ]
       }
     }
   },
   ...
 ]
}

The fetchAndJoinCustomAudience() API determines the buyer's identity from the eTLD+1 of fetchUri. The CustomAudience buyer's identity is used to perform the same enrollment and app authorization checks done by joinCustomAudience() and cannot be altered by the fetch response. The CustomAudience owner is the package name of the calling application. There is no other validation of fetchUri other than the eTLD+1 check, and in particular, there is no k-anon check. The fetchAndJoinCustomAudience() API issues an HTTP GET request to fetchUri and expects a JSON object representing the custom audience. The same mandatory, optional constraints and default values for the custom audience object fields are applied to the response. Learn about current requirements and limitations in our Developer Guide.

Any HTTP error response from the buyer causes fetchAndJoinCustomAudience to fail. In particular an HTTP status response of 429 (Too Many Requests) blocks requests from the current application for a to-be-defined period. The API call also fails if the response from the buyer is malformed. Failures are reported to the API caller responsible for retrying due to temporary failures (such as the server not responding) or handling persistent failures (such as data validation failures or other non-transitory errors with communication to the server).

The CustomAudienceFetchRequest object allows the API caller to define some information for the Custom Audience by using the optional properties shown in the example above. If set in the request, these values cannot be overwritten by the buyer response received by the platform; the Protected Audience API ignores the fields in the response. If they are not set in the request, then they must be set in the response, as these fields are required to create a custom audience. A JSON representation of the content of the CustomAudience as partially defined by the API caller is included in the GET request to fetchUri in a special header X-CUSTOM-AUDIENCE-DATA. The size of the serialized form of the data specified for the Custom Audience is limited to 8KB. If the size is exceeded the fetchAndJoinCustomAudience API call fails.

The lack of a k-anon check allows you to use fetchUri for buyer verification and to enable information sharing between buyer and SDK. To facilitate custom audience verification, it is possible for the buyer to provide a verification token. The on device SDK should include this token in fetchUri so that the endpoint hosted by the buyer can fetch the contents of the custom audience and use the verification token to verify that the fetchAndJoinCustomAudience() operation corresponds to the buyer and originated from a trusted on-device partner. To share information, the buyer can agree with the on-device caller that some of the information to be used to create the custom audience will be added as query parameters to the fetchUri. This allows the buyer to audit the calls and detect if a validation token has been used by a malicious ad tech to create several different custom audiences.

A note on verification token definition and storage

  • The verification token is not used for any purposes by the Protected Audience API and is optional.

    • The verification token may be used by the buyer to verify that audiences being created are being done on their behalf.
    • The Protected Audience API proposal specifies neither a format for the verification token nor how the buyer transfers the verification token to the caller. For example, the verification token could be pre-loaded in the owner's SDK or backend, or it could be retrieved real-time by the owner's SDK from the buyer's server.

Leave a custom audience

The owner of a custom audience may choose to leave by calling leaveCustomAudience(), as shown in the illustrative code snippet below:

// Invoke ad services API to leave a custom audience.
leaveCustomAudience(buyer, name);

To help conserve usage of storage and other device resources, custom audiences expire and are removed from the on-device store after a predetermined period of time. The default value is to be determined. The owner can override this default value.

User control

  • The proposal intends to give users visibility to the list of installed apps that have created at least one custom audience
  • Users can remove apps from this list. The removal clears all the custom audiences associated with the apps and prevent the apps from joining new custom audiences.
  • Users have the ability to reset the Protected Audience API completely. When this happens, any existing custom audiences on the device are cleared.
  • Users have the ability to opt-out completely from the Privacy Sandbox on Android, which includes the Protected Audience API. If the user has opted out of the Privacy Sandbox, the Protected Audience API fails silently.

The design of this capability is a work in progress, and the details will be included in a later update.

Scheduled Updates

The solutions previously described require the app or ad tech SDK to invoke the APIs while the app is in the foreground and provide the full properties of the custom audience, either directly or using delegation. However, it is not always possible for advertisers and ad tech providers to define which audiences a user belongs to in real-time as they use the app.

To facilitate this, it is possible for ad tech to call the scheduleCustomAudienceUpdate() API. This API allows the caller to specify a delay on when the API call should be made, therefore providing additional time for the responding ad tech to process app-level events and determine which Protected Audiences the user should join or be removed from.

/**
* API To schedule delayed update events for Custom Audience
*
* @param request Criteria that determine when and where to trigger a call to a
* DSP endpoint to update one or more Custom Audiences
*/

public void scheduleCustomAudienceUpdate(
    @NonNull ScheduleCustomAudienceUpdateRequest request,
    @NonNull @CallBackExecutor Executor executor,
    @NonNull AdServicesOutcomeReceiver<Object, Exception> receiver)

ScheduleCustomAudienceUpdateRequest

public final class ScheduleCustomAudienceUpdateRequest {
  // Required Field
  @NonNull public Uri getUpdateUri() {
    return mUpdateUri;
  }

  // Required Field
  @NonNull public Duration getMinDelay() {
    return mMinDelay;
  }

  //  Required Field
  @NonNull public List<PartialCustomAudience> getPartialCustomAudienceList() {
    return mPartialCustomAudiences;
  }

  //  Optional Field (default false)
  public boolean shouldReplacePendingUpdates () {
    return mShouldReplacePendingUpdates;
  }
}

The ScheduleCustomAudienceUpdateRequest contains the information necessary for registering a delayed job to run with the platform. After the specified delay, a background job will run periodically and send the request(s). The ScheduleCustomAudienceUpdateRequest can contain the following information:

  • UpdateUri: URI endpoint that would be sent a GET call to fetch the update. Buyer identity is intrinsically inferred from the eTLD+1 and need not be explicitly provided and cannot be altered by the update response. The GET request expects a JSON object containing a list of customAudience objects in return.
  • DelayTime: Time signifying the delay from the time of making the scheduleCustomAudienceUpdate() API call to schedule the update.
  • PartialCustomAudience: The API also allows the on-device SDK to send a list of partially constructed Custom Audiences. This allows the in-app SDKs to serve the dual role, from complete to partial control over Custom Audience management based on their partnership with DSPs.

    • This also keeps this API compatible with the fetchAndJoinCustomAudience() API which allows for similar information sharing.
  • ShouldReplacePendingUpdates: Boolean that determines if any pending scheduled updates should be canceled and replaced with the update detailed in the current ScheduleCustomAudienceUpdateRequest. If this set to false and there are previously requested updates still pending for the same buyer in the same app, a call to scheduleCustomAudienceUpdate with this ScheduleCustomAudienceUpdateRequest will fail. Defaults to false.

App permissions and control

The proposal intends to provide apps control over its custom audiences:

  • An app can manage its associations with custom audiences.
  • An app can grant third-party ad tech platforms permissions to manage custom audiences on its behalf.

The design of this capability is a work in progress, and the details will be included in a later update.

Ad tech platform control

This proposal outlines ways for ad techs to control their custom audiences:

  • Ad techs enroll with the Privacy Sandbox and provide an eTLD+1 domain which matches all URLs for a custom audience.
  • Ad techs can partner with apps or SDKs to provide verification tokens that are used to verify a custom audience creation. When this process is delegated to a partner, custom audience creation can be configured to require acknowledgement by the ad tech.
  • An ad tech can choose to deactivate joinCustomAudience calls on their behalf and only allow the fetchAndJoinCustomAudience API to enable all call custom audiences. Control can be updated during Privacy Sandbox enrollment. Note the control allows either all ad techs or none. Due to platform limitations, delegation permissions can't be on a per ad tech basis.

Candidate ads and metadata response

Candidate ads and metadata returned from a buy-side platform should include the following fields:

  • Metadata: Buy-side, ad tech-specific ads metadata. For example, this may include information about the ad campaign, and targeting criteria such as location and language.
  • Render URL: Endpoint for rendering the ad creative.
  • Filter: Optional information necessary for the Protected Audience API to filter ads based on on-device data. For more details, read the section on buy side filtering logic.

Ad selection workflow

This proposal aims to improve privacy by introducing the Ad Selection API, which orchestrates auction execution for ad tech platforms.

Ad tech platforms today typically perform bidding and ad selection exclusively on their servers. With this proposal, custom audiences and other sensitive user signals, such as available installed package information, will be accessible only through the Ad Selection API. Additionally for the remarketing use case candidate ads will be fetched out of band (i.e. not in the context in which ads will be shown). Ad tech platforms will need to prepare to have some parts of their current auction and ad selection logic deployed and executed on the device. Ad tech platforms may consider the following changes to their ad selection workflow:

  • Without installed package information available on the server, ad tech platforms may want to send multiple contextual ads back to the device and invoke the ad selection workflow to enable app install-based filtering in order to maximize chances to show a relevant ad.
  • Because remarketing ads are fetched out of band, current bidding models may need to be updated. Ad tech platforms may want to create bidding sub-models (the implementation may be based on a pattern called two-tower model) that can work on ads features and contextual signals separately and combine the sub-model outputs on the device to predict bids. This can benefit from both server-side auctions and auctions for any given ad opportunity.

This approach enables the user's app interactions data to inform ads selection, while limiting the sharing of this data with third-parties.

Flow chart that shows the initiation of the ad selection workflow.

This ad selection workflow orchestrates the on-device execution of ad tech-provided JavaScript code based on the following sequence:

  1. Buy-side bidding logic execution
  2. Buy-side ad filtering and processing
  3. Sell-side decision logic execution

For ad selections that involve custom audiences, the platform will fetch buy side-provided JavaScript code based on the public URL endpoint defined by the custom audience's "Bidding logic URL" metadata. The URL endpoint for sell-side decision code will also be passed as an input to initiate the ad selection workflow.

The design of ad selections that don't involve custom audiences is under active design.

Initiate ad selection workflow

When an app needs to show an ad, the ad tech platform SDK may initiate the ad selection workflow by calling the selectAds() method after instantiating the AdSelectionConfig object with the expected parameters:

  • Seller: Identifier for the sell-side ad platform, following the eTLD+1 format
  • Decision logic URL: When an ad auction is initiated, the platform will use this URL to fetch JavaScript code from the sell-side platform to score a winning ad.
  • Custom audience buyers: A list of buy-side platforms with custom audience-based demand for this auction, following the eTLD+1 format.
  • Ad selection signals: Information about the auction (ad size, ad format etc.).
  • Seller signals: Supply side platform specific signals.
  • Trusted Scoring Signals URL: URL endpoint of sell-side trusted signal from which creative specific realtime information can be fetched from.
  • Per buyer signals: Participating demand sides may use this parameter to provide inputs for the auction. For example, this parameter may include comprehensive contextual information useful for determining bids.

The following illustrative code snippet shows an ad tech platform SDK initiating the ad selection workflow by first defining the AdSelectionConfig and then invoking selectAds to get the winning Ad:

AdSelectionConfig myAdSelectionConfig = new AdSelectionConfig {
    Seller = "example-ssp1.com",
    DecisionLogicURL = Uri.parse("https://..."),
    CustomAudienceBuyerList = Arrays.asList("example-dsp1.com","bexample-dsp2.com"),
    AdSelectionSignals = "{"min_price": 10,"auction_attempts": 3}"
    SellerSignals = "{"seller_type": "news", "content_category": "sports","mature_ads_accepted" :"false"}"
    PerBuyerSignals = " {"buyer1Name": {"key1" : "value1"},
                         "buyer2Name": {"key1" : "value1", "key2" : "value2" }"
};

// Invoke ad services API to initiate ad selection workflow.
Ad winningAd = selectAds(myAdSelectionConfig);

Buy-side bidding logic

The bidding logic is typically provided by buy-side platforms. The purpose of the code is to determine bids for candidate ads. It may apply additional business logic to determine the result.

The platform will use the custom audience's "Bidding logic URL" metadata to fetch the JavaScript code which should include the function signature below:

generateBid(ad, auction_signals, per_buyer_signals, trusted_bidding_signals,
        contextual_signals, user_signals, custom_audience_signals) {
    // ...
    return {'bid': ...};
}

The generateBid() method returns the calculated bid amount. The platform will invoke this function for all ads (contextual or remarketing) sequentially. If there are multiple bidding logic providers, the system does not guarantee the execution sequence among the providers.

The function expects the following parameters:

  • Ad: The ad being considered by the buy-side bidding code. This will be an Ad from an eligible custom audience
  • Auction signals: Sell-side, platform-specific signals.
  • Per buyer signals: Participating demand sides may use this parameter to provide inputs for the auction. For example, this parameter may include comprehensive contextual information useful for determining bids.
  • Trusted bidding signals: Ad tech platforms rely on real-time data to inform ad retrieval and scoring. For instance, an ad campaign may run out of budget and needs to be stopped serving immediately. An Ad-tech can define a URL endpoint from where this real-time data can be fetched and the set of keys for which the real-time lookup needs to be performed. The ad tech platform's managed server that serves this request will be a trusted server managed by the ad tech platform.
  • Contextual signals: This may include coarsened timestamp or approximate location information, or cost per click on the ad.
  • User signals: This may include information such as available installed package information.

Ad cost

In addition to the bid, buy-side platforms have the option to return the cost per click as part of generateBid(). For example:

generateBid(ad, auction_signals, per_buyer_signals, trusted_bidding_signals,
        contextual_signals, user_signals, custom_audience_signals) {
    // ...
    return {'bid': ..., 'adCost': ...,};
}

If this ad is the winner, adCost is stochastically rounded to 8 bits for privacy. The rounded value of adCost is then passed into the contextual_signals parameter in reportWin during impression reporting.

Buy-side filtering logic

Buy-side platforms will be able to filter ads based on additional on-device signals available during the ad selection phase. For example, ad tech platforms can implement frequency capping capabilities here. If there are multiple filtering providers, the system does not guarantee the execution sequence among the providers.

The buy-side filtering logic can be implemented as part of the bidding logic by returning a bid value of 0 for a given ad.

In addition to that, buy-side platforms will be able to signal that a given ad should be filtered based on additional on-device signals available to Protected Audience and that won’t leave the device. As we solidify the designs of additional filtering logic, buy-side platforms will follow this same structure to signal that the filtering should happen.

Sell-side scoring logic

The scoring logic is typically provided by the sell-side platform. The purpose of the code is to determine a winning ad based on bidding logic outputs. It may apply additional business logic to determine the result. If there are multiple decision logic providers, the system does not guarantee the execution sequence among the providers. The platform will use the "Decision logic URL" input parameter of the selectAds() API to fetch the JavaScript code which should include the function signature below:

scoreAd(ad, bid, auction_config, trusted_scoring_signals,
        contextual_signals, user_signals, custom_audience_signals) {
    // ...
    return score_for_this_ad;
}

The function expects the following parameters:

  • Ad: The Ad being evaluated; output from the generateBid() function.
  • Bid: Bid amount output from the generateBid() function.
  • Auction config: Input parameter to selectAds() method.
  • Trusted Scoring Signals: Ad tech platforms rely on real-time data to inform ad filtering and scoring. For instance, an app publisher may block an ad campaign from showing ads in the app. This data is fetched from trusted scoring signals url parameter of the auction configuration. The server serving this request should be an ad tech-managed trusted server.
  • Contextual signal: This may include coarsened timestamp or approximate location information.
  • User signal: This may include information such as the app store that initiated the installation of the app.
  • Custom audience signal: If the Ad being scored is coming from an on-device custom audience, this will contain information such as the reader and name of the custom audience.

Ad selection code runtime

In the proposal, the system will fetch ad tech platform-provided auction code from configurable URL endpoints and execute on the device. Given the resource constraints on mobile devices, auction code should adhere to the following guidelines:

  • The code should finish executing in a predefined amount of time. This bound will apply uniformly to all buyer ad networks. Details of this bound will be shared in a later update.
  • The code must be self-contained and not have any external dependencies.

Since the auction code, such as the bidding logic may need access to private user data such as app install sources, the runtime will not provide network or storage access.

Programming language

Ad tech platform-provided auction code should be written in JavaScript. This would allow ad tech platforms to, for example, share bidding code across platforms that support Privacy Sandbox.

Winning ad rendering

The ad with the highest score is considered the winner of the auction. In this initial proposal, the winning ad is passed into the SDK for rendering.

The plan is to evolve the solution to ensure that information about a user's custom audience membership, or app engagement history, cannot be determined by the app or SDK through information about the winning ad (similar to Chrome's fenced frames proposal).

Impression and event reporting

Once the ad has been rendered, the winning impression can be reported back to participating buy-side and sell-side platforms. This allows buyers and sellers to include information from the auction, such as the bid or custom audience name, with the winning impression report. Additionally, the sell-side and winning buy-side platform are eligible to receive additional event-level reporting about the winning ad. This provides the ability to include information about the auction (bid, custom audience name etc.) with clicks, views and other ad events. The platform invokes reporting logic in this order:

  1. Sell-side reporting.
  2. Buy-side reporting.

This gives buy-side and sell-side platforms a way to send important on-device information back to the servers to enable capabilities like real time budgeting, bidding model updates, and accurate billing workflows. This impression reporting support is complementary to the Attribution Reporting API.

There are two steps required to support event reporting: Sell-side and buy-side JavaScript needs to register what event it should receive event reports for, and the sell-side is responsible for reporting event-level information.

Protected Audience provides a mechanism to subscribe to future events related to a winning auction by registering beacons. In a seller's reportResult() JavaScript function, sell-side platforms can register beacons using the platform's registerAdBeacon() function. Similarly, buy-side platforms can call the registerAdBeacon() method from their reportWin() JavaScript function.

registerAdBeacon(beacons)

Input:

  • event_key: A string denoting which interaction type to register for. This is used as a key to look up the end point the platform pings while reporting the results of the auction.
  • reporting_url: The URL owned by the ad tech platform for handling the event.

Event keys are string identifiers that are owned by the sell-side SDK responsible for reporting the results of the auction. For a callback to be made, ad techs register beacons with keys that match the keys used by the sell-side when reporting the events. These don't need to be k-anonymous, though there are limits to the quantity and length of keys that can be registered for a given custom audience. If reportEvent() is called, sell-side platforms that ran the auction are always eligible to receive these event reports. Only the winning buy-side platform is eligible to receive these reports.

Sell-side reporting

The platform invokes the reportResult() JavaScript function in the supply side-provided code downloaded from the seller's Decision logic URL parameter for the selectAds() API:

reportResult(render_url, bid, auction_config, contextual_signals) {
    // ...
    beacons = {"click":clickUri}
    registerAdBeacon(beacons)
    return {
      "status": 0,
      "results": {"reporting_url": reporting_url,
                  "signals_for_buyer": signals_for_buyer}};
}

Output: A JSON object containing

  • Status: 0 for success, any other value for failure.
  • Reporting URL: The platform invokes this URL returned from the function.
  • Signals for Buyer: A JSON object to be passed to the buyer's reportWin function.

The supply side may encode relevant signals in the reporting URL to help them gain further insights into the auction and winning ad. For example, it may include signals below:

  • Ad render URL
  • Winning bid amount
  • App name
  • Query identifiers
  • Signals for buyer: To support data sharing between supply side and demand side, the platform passes this return value as an input parameter to demand side reporting code.

Buy-side reporting

The platform invokes the reportWin() JavaScript function in the demand side-provided code downloaded from the Bidding logic URL metadata of the custom audience associated with the auction.

reportWin(render_url, bid, auction_signals, per_buyer_signals,
        signals_for_buyer, contextual_signals, custom_audience_signals) {
    // ...
    beacons = {"click":clickUri}
    registerAdBeacon(beacons)
    return {
      "status": 0,
      "results": {"reporting_uri": reporting_uri}};
}

Input:

  • auction_signals and per_buyer_signals are fetched from AuctionConfig. Any information that the buy-side platform needs to pass into the reporting URL may come from this datum.
  • signals_for_buyer is the output of the sell-side reportResult. This provides the sell-side platform with an opportunity to share data with the buy-side platform for reporting purposes.
  • contextual_signals contains information such as app name and custom_audience_signals contains the custom audience information. Other information may be added in the future.

Output:

  • Status: 0 for success, any other value for failure.
  • Reporting URL: The platform invokes this URL returned from the function.

Reporting Events

Reporting events is only possible after impression reporting for the auction has completed. The sell-side SDK is responsible for reporting any events. The platform exposes an API that takes a ReportEventRequest that specifies the recently run auction, the event key that is reported, the data associated with that key, whether the report should be sent to the buyer or the seller (or both), and an optional input event for ad events. The client defines the event key and collection of data to report.

ReportEventRequest request = new ReportEventRequest(
  AdSelectionId = ad_selection_id,
  event_key = "view"
  event_data = "{ "viewTimeInSeconds" :1 }",
  reporting_destinations =
    FLAG_REPORTING_DESTINATION_SELLER |
      FLAG_REPORTING_DESTINATION_BUYER,
  input_event = clickInputEvent // or null for view
  )

reportEvent(request)

Input:

  • ad_selection_id needs to be an AdSelectionId of a recently run auction retrieved from the AdSelectionOutcome.
  • event_key is a sell-side defined string describing the interaction event.
  • event_data is a string representing the data associated with the event_key
  • reporting_destinations is a bit mask set using flags provided by the platform. It can be one of FLAG_REPORTING_DESTINATION_SELLER or FLAG_REPORTING_DESTINATION_BUYER, or both.
  • input_event (optional) is used for integration with Attribution Reporting API. It is either an InputEvent object (for a click event) or null (for a view event). See Attribution Reporting API Integration for more details on this parameter.

After the sell-side SDK invokes reportEvent and, depending on the reporting_destinations flag, the platform attempts to match the event_key to the keys registered by buyers and sellers in their reportWin and reportResult JavaScript functions. If there is a match, the platform POSTs the event_data to the associated reporting_url. The content type of the request is plain text with the body being the event_data. This request is made as best effort and fails silently in the event of a network error, server error response, or if no matching keys were found.

Attribution Reporting API integration

To support buyers who participate in a Protected Audience auction, we are providing cross-API functionality across the Protected Audience and Attribution Reporting APIs (ARA). This functionality enables ad techs to evaluate their attribution performance across various remarketing tactics, so that they can understand which types of audiences deliver the highest ROI.

Through this cross-API integration, adtechs can:

  • Create a key-value map of URIs to be used for both 1) ad interaction reporting and 2) source registration.
  • Include auction data from the Protected Audience auction in their source-side key mapping for aggregate summary reporting (using the Attribution Reporting API) See the ARA design proposal for more information.

When a user sees or clicks on an ad:

  • The URL used to report those events interactions using Protected Audience will provide the necessary data to the buyer to be used to register a view or click as an eligible source with the Attribution Reporting API.
  • The ad tech may choose to pass CustomAudience (or other relevant contextual information about the ad such as ad placement or view duration) using that URL so this metadata can propagate down to summary reports when the ad tech is reviewing aggregate campaign performance.

Enabling source registration

reportEvent() will accept a new optional parameter InputEvent. Winning buyers who register ad beacons can choose which reportEvent reports get registered with Attribution Reporting APIs as a registered source. The request header Attribution-Reporting-Eligible will be added to all event reporting requests sent from reportEvent(). Any responses with appropriate ARA headers will be parsed in the same way any other regular ARA source registration response would be. See Attribution Reporting API explainer for how to register a source URL.

Because ARA on Android supports view and click events, InputEvents are used to distinguish between the two types. Just as in ARA source registration, the reportEvent() API will interpret a platform-verified InputEvent as a click event. If the InputEvent is missing, null, or invalid, the source registration will be considered a view.

Note the post-auction eventData may contain sensitive information, so the platform drops the eventData in redirected source registration requests.

Engagement and conversion reporting example

In this example, we'll look at it from the buyer perspective who is interested in associating the data from the auction, rendered ad, and conversion app together.

In this workflow, the buyer coordinates with the seller to send a unique ID into the auction. During the auction, the buyer sends this unique ID with the auction data. During render and conversion time, the data from the rendered ad is also sent out with the same unique ID. Later, the unique ID can be used to associate these reports together.

Workflow: Before the auction starts, the buyer sends a unique ID to the seller as part of their programmatic real-time bidding ("RTB") bid response. The ID can be set as a variable like auctionId. The ID is passed in as perBuyerSignals in the auctionConfig and it becomes available in buyer's bidding logic.

  1. During the execution of reportWin, the buyer can register an ad beacon to be triggered during ad render time and for specific interaction events (registerAdBeacon()). To associate auction signals for an ad event, set the auctionId as a query param of the beacon URL.
  2. During ad render time, the beacons you registered during auction time can be triggered or enhanced with event-level data. The seller must trigger reportEvent() and pass in the event-level data. The platform will ping the buyer's registered ad beacon URL that correlates with the reportEvent() that was triggered.
  3. The buyer will register the ad with the Attribution Reporting API by responding to the ad beacon requests with the Attribution-Reporting-Register-Source header. To associate auction signals for a conversion event, set the auctionId in the Register source URL.

After the above process, the buyer will have an auction report, interaction report, and conversion report, that can be correlated using the unique ID that can be used to associate with each other.

Similar workflow applies to a seller if it needs access to attribution data, and the seller can also use a unique ID to send with registerAdBeacon(). The reportEvent() call contains a destination property that can be used to send the report to both the buyer and the seller.

Ad tech platform managed trusted server

Ad selection logic today requires real-time information such as budget depletion status to determine if ad candidates should be selected for auction. Both buy-side and sell-side platforms will be able to get this information from servers they operate. In order to minimize leak of any sensitive information via these servers the proposal calls for the following restrictions:

  • The behaviors of these servers, described later in this section, would not leak user information.
  • The servers would not create pseudonymous profiles based on the data it sees, i.e. it will need to be ‘trusted'.

Buy side: Once buy side initiates the buy side bidding logic, the platform performs an HTTP fetch of trusted bidding data from the trusted server. The URL is composed by appending the URL and keys present in the Trusted Bidding Signals metadata of the custom audience being processed. This fetch is done only when processing ads from the on device custom audiences. At this stage, buy side can enforce budgets, check for campaign pause / unpause state, perform targeting, etc.

Below is a sample URL to fetch trusted bidding data, based on trusted bidding signal metadata from the custom audience:

https://www.kv-server.example/getvalues?keys=key1,key2

The response from the server should be a JSON object whose keys are key1, key2, etc., and whose values will be made available to the buyer's bidding functions.

Sell side: Similar to the buy side flow above, sell side may want to fetch information about creatives considered in the auction. For instance, a publisher may want to enforce that certain creatives are not shown in their app based on brand safety concerns. This information can be fetched and made available to the sell side auction logic. Similar to the buy side trusted server lookup, sell side trusted server lookup also happens via an HTTP fetch. The URL is composed by appending the Trusted Scoring Signals URL with render URLs of the creatives for which the data needs to be fetched.

Below is a sample URL to fetch information about creatives considered in the auction, based on creative render URLs:

https://www.kv-server.example/getvalues?renderUrls=render_url1,render_url2

Response from the server should be a JSON object whose keys are render URLs sent in the request.

These servers would operate in a trusted way to offer several security and privacy benefits:

  • The server's return value for each key can be trusted to be based only on that key.
  • The server does not perform event-level logging.
  • The server has no other side effects based on these requests.

As a temporary mechanism, the buyer and seller can fetch these bidding signals from any server, including one they operate themselves. However, in the release version, the request will only be sent to a trusted key-value-type server.

Buyers and sellers could use a common trusted key-value-type server for platforms compatible with Privacy Sandbox on Android and for the Web.