Integrate with B&A as a buyer

Bidding and Auction (B&A) Services is a set of services for ad buyers and sellers that runs in a Trusted Execution Environment (TEE) to facilitate a Protected Audience (PA) auction. This Developer's Guide explains how a buyer can integrate with a B&A PA auction for Chrome.

Overview

To participate in a Protected Audience auction with B&A Services, the buyer updates the interest group (IG) to optimize the payload for improved auction latency.

The following payload optimization tasks are required by the buyer:

Interest group for B&A

The following is an example interest group config for a B&A PA auction with payload optimization applied:

navigator.joinAdInterestGroup({
  name: 'example-ig',
  owner: 'https://dsp.example',

  // An ID is mapped to each render URL
  ads: [
    {
      renderURL: 'https://dsp.example/ad.html',
      adRenderId: '12345678' // 12 characters max,
      buyerReportingId: 'brid123', // Optional
      buyerAndSellerReportingId: 'bsrid123', // Optional
      selectableBuyerAndSellerReportingId: ['sbsrid123', 'sbsrid456'], // Optional
    },
  ],
  adComponents: [
    {
      renderURL: 'https://dsp.example/ad-component.html',
      adRenderId: 'abcdefgh'
    },
  ],

  // Flags are set to omit data in the B&A auction payload
  auctionServerRequestFlags: ['omit-ads', 'omit-user-bidding-signals'],

  // Data not included in the B&A auction payload can be fetched as trusted signals
  // The following is an example of how the keys could look, but the actual
  // implementation is up to the ad tech
  trustedBiddingSignalsKeys: [
    'exampleUserBiddingSignalsKey',
    'exampleAdRenderIdKey',
    'exampleAdMetadataKey',
    'exampleAdReportingIdKey',
  ],

  // Optionally, interest groups can be prioritized
  priority: 0.0,
});

The differences between the B&A and on-device interest group configs are:

Fields B&A IG On-device IG Included in the B&A auction payload
auctionServerRequestFlags Used Not used No
userBiddingSignals Not recommended Used No, if omit-user-bidding-signals flag is set
adRenderId in ads and adComponents Used Not used If the omit-ads flag is set, adRenderId in ads is available only in browserSignals.prevWins of the payload. adRenderId defined in adComponents is not included in the payload.

If the omit-ads flag is not set, available in browserSignals.prevWins, interestGroup.adRenderIds and interestGroup.adComponentRenderIds.

renderURL in ads and adComponents Used Used No
metadata in ads and adComponents Not used Used No
Reporting IDs in ads Used Used No
  • The auctionServerRequestFlags field allows setting flags that tells the browser to omit some data in the B&A auction payload.
  • The userBiddingSignals value can be defined in the interest group, but it is recommended to omit them by using the omit-user-bidding-signals flag. The omitted signals can be supplied by using the K/V Service.
  • The adRenderId field is set along with the associated renderURL, but only the adRenderId will become part of the B&A auction payload. The render URL returned from generateBid() later during auction time must match the render URL defined in the IG.
  • Reporting IDs are defined in the B&A IG, but are not included in the B&A auction payload. The reporting ID returned from generateBid() later during auction time must match the render URL defined in the IG.
  • The ad.metadata and reporting IDs are not included in the B&A auction payload, and instead, those data become available through the Trusted Key/Value Service usage.

Note that renderURLs and reporting IDs in ads are still defined in the interest group config, though they don't become included in the auction request payload, because the browser checks that the render URL and reporting IDs returned from generateBid() function of the Bidding Service match the values defined in the interest group.

joinAdInterestGroup() tasks

The following tasks need to be performed for the joinAdInterestGroup() call.

Set server request flags

The joinAdInterestGroup() config's auctionServerRequestFlags field accepts the following flags:

Flag Description
omit-user-bidding-signals The omit-user-bidding-signals flag omits the userBiddingSignals object in the auction payload.

If the flag is not set, the userBiddingSignals value defined in the interest group will become available inside generateBid() of the Bidding Service.

omit-ads The omit-ads flag tells the browser to omit the ads and adComponents objects in the auction payload.

The adRenderId will be available in the prevWins property of browserSignals.

If the flag is not set, adRenderIds and adComponentRenderIds fields in the interestGroup argument of generateBid() will contain the corresponding ad render IDs.

It is highly recommended for buyers to opt for the omit-ads flag. At some point in the future, passing render IDs and ad component render IDs from the client may be deprecated for further payload optimization.

The omitted data is handled by making relevant information available in trustedBiddingSignals. The flags can be used individually, and don't have to be used together.

Example usage:

navigator.joinAdInterestGroup({
  auctionServerRequestFlags: ['omit-user-bidding-signals', 'omit-ads'],
});

Set ad render IDs

To reduce the size of the B&A auction payload, the ads and adComponents objects of the interest group are omitted, and in turn, these objects are not available inside the generateBid() function running in Bidding Service.

To handle the missing ad information, the buyer maintains an identifier (adRenderId and adComponentRenderId) associated with each ad in the interest group config. The identifier must be a DOMString that is 12 bytes long or less. If the identifier is Base64 encoded, its length must be 12 bytes long or less.

An example interest group with ad render IDs:

navigator.joinAdInterestGroup({
  ads: [
    {
      renderURL: 'https://dsp.example/ad.html',
      adRenderId: '12345678' // 12 characters max
    },
  ],
  adComponents: [
    {
      renderURL: 'https://dsp.example/ad-component.html',
      adComponentRenderId: 'abcdefgh'
    },
  ],
});

The adRenderId associated with the ads become available in prevWins.browserSignals in generateBid().

Though the renderURL is not included in the request payload, the returned render URL from generateBid() must match the render URL defined in the interest group config. Ad techs can send back ad metadata and other information in trustedBiddingSignals, so that ad render URL and ad component render URL can be generated for the bid during the generateBid() execution.

Set interest group priorities

Chrome allows buyers to prioritize interest groups. If the per-buyer payload size limit set by the seller is reached, then the interest group priority values are used to drop the lower priority interest groups for a single buyer when the B&A auction payload is generated for the seller. For selecting interest groups between different buyers, the browser decides based on the size of the serialized payload. By default, each buyer is given an equal size. Note that the actual prioritization occurs on the B&A servers, and not when the request payload is generated.

The priority is calculated at auction time by using the buyer's priority vectors (priorityVector) and the seller's priority signals (prioritySignals). The buyer has the ability to override the seller's priority signals.

Property Description
Priority vector The buyer supplies the vectors as the value of the priorityVector key from the K/V Service
Priority signals The seller supplies the signals by setting priority_signals of the auction config
Priority signals overrides The buyer supplies the override in the priority_signals_overrides field of the PerBuyerConfig in the auction config.

During the auction, the browser calculates the sparse dot product of the matching keys in priorityVector and prioritySignals for the priority. In the following diagram, the priority is calculated by (4 * 2) + (3 * -1) which is reduced to 8 + -3, so the priority of this interest group at auction time is 5.

Each key in the priority vector and priority signals objects are multiplied by each other, then the results are summed together to calculate the priority
Figure 1: Priority calculation using buyer's vectors and seller's signals

Additional signals are also available to be used for prioritization in B&A:

Signal Description
deviceSignals.one The value is always 1, and is useful for adding a constant to the dot product.
deviceSignals.ageInMinutes The value describes the interest group's age (the time since the most recent interest group join) in minutes as an integer between 0 and 43,200.
deviceSignals.ageInMinutesMax60 The value describes the same as the ageInMinutes signal, but is maxed at 60. If the group is more than 1 hour old, then 60 is returned.
deviceSignals.ageInHoursMax24 The value describes the interest group's age in hours, maxed at 24 hours. If the group is more than a day old, then 24 is returned.
deviceSignals.ageInDaysMax30 The value describes the interest group's age in days, maxed at 30 days. If the group is more than 30 days old, then 30 is returned.

To learn more, visit the explainer on GitHub.

Set up trusted bidding signals

Since some data will be omitted from the B&A auction payload, you can use the Key/Value Service to supply the omitted data as trusted bidding signals to the generateBid() function.

The following omitted data can be supplied by the K/V Service:

  • userBiddingSignals if used by the buyer
  • metadata associated with each ad
  • adRenderId associated with each ad
  • Reporting ID
The omitted data from the interest group can be sent to the buyer's collection server. The collection server pushes the data to the key/value service, and at a later time, the browser loads those data from the key/value service
Figure 2: Example trusted signals setup

One approach that can be taken is to include a unique identifier in the trusted bidding signals keys, and then send the associated data to your server so it can be loaded into the Key/Value Service. However, the actual implementation is up to the ad tech and the API is not prescriptive.

The following example describes one approach that can be implemented:

const ad1RenderURL = 'https://dsp.example/ad-1.html';
const ad2RenderURL = 'https://dsp.example/ad-2.html';
const ad1RenderId = 'render-id-1';
const ad2RenderId = 'render-id-2';
const ad1ReportingId = 'reporting-id-1';
const ad2ReportingId = 'reporting-id-2';

// Generate a unique identifier
const id = crypto.randomUUID();

// Define the keys with the unique ID
const trustedSignalsKeyForIG = `interest-group-${id}`

// Set the keys in the interest group
navigator.joinAdInterestGroup({
  // …
  ads: [
    {
      renderURL: ad1RenderURL,
      adRenderId: ad1RenderId,
      buyerReportingId: ad1ReportingId
    },
    {
      renderURL: ad2RenderURL,
      adRenderId: ad2RenderId,
      buyerReportingId: ad2ReportingId
    },
  ],
  trustedBiddingSignalsKeys: [
    trustedSignalsKeyForIG
  ]
});

// Send the associated data to your server to be loaded into the Key/Value Service
fetch('https://dsp.example/kv/load', {
  method: 'POST',
  body: JSON.stringify({
    id,
    [trustedSignalsKeyForIG]: {
      userBiddingSignals: {
        favoriteColor: 'blue'
      },
      ads: [
        {
          renderURL: ad1RenderURL,
          adRenderId: ad1RenderId,
          buyerReportingId: ad1ReportingId,
          metadata: {
            color: 'red'
          }   
        },
        {
          renderURL: ad2RenderURL,
          adRenderId: ad2RenderId,
          buyerReportingId: ad2ReportingId,
          metadata: {
            color: 'blue'
          }   
        },
      ]
    }
  })
});

In the example, a unique identifier is defined for an IG and becomes part of the trusted signals key. The key for the IG and their associated values are sent to your server to be loaded into the Key/Value Service. At a later time during the auction, the browser fetches the trusted signals and makes them available in the buyer's generateBid() function.

Return interest group update signal from K/V if needed

The updateIfOlderThanMs key for the trusted signals is used to update the interest group earlier than the usual daily interval. If the interest group hasn't been joined or updated in a duration of time exceeding the milliseconds value returned for the updateIfOlderThanMs key, the interest group will be updated with the updateURL mechanism. Note that Chrome won't update interest groups more frequently than once every 10 minutes.

If the B&A auction returns a winning ad that doesn't match one of the ads defined in the interest group stored in the browser, then the browser will fail the auction. The updateIfOlderThanMs mechanism can be useful in ensuring that the browser and the B&A auction agree on the set of ads in the interest group.

Visit the explainer to learn more.

generateBid() tasks

The following tasks need to be performed for the generateBid() call.

Read browser signals

The browserSignals object passed into the B&A generateBid() call looks like the following:

{
  topWindowHostname: 'advertiser.example',
  seller: 'https://ssp.example',
  topLevelSeller: 'https://ssp-top.example',
  joinCount: 5,
  bidCount: 24,
  recency: 1684134092,

  // prevWins is [timeInSeconds, adRenderId]
  prevWins: [
    [9342, 'render-id-1'],
    [1314521, 'render-id-2']
  ],

  // Compiled WebAssembly code
  wasmHelper: WebAssembly.Module

  // Data-Version value from K/V response, if available
  dataVersion: 1,
}

The following modified or new properties are available in browserSignals:

Property Description
prevWins prevWins is an array of tuples of time and ad. The time represents the seconds passed since the associated ad's previous win in the last 30 days.

It has been modified to provide the adRenderId instead of the ad object.

wasmHelper Compiled object of the code supplied from biddingWasmHelperURL.
dataVersion A trusted server may optionally include a numeric Data-Version response header that becomes available in generateBid().

Read the explainer on GitHub to learn more.

Return render URL from generateBid()

Since the ads object is omitted in the B&A auction payload, the render URL returned from generateBid() must be recreated. How the render URL is recreated is determined by your implementation, and the returned URL must match the render URL defined in the interest group.

One approach that could be taken is to maintain a base URL, and populate the template with the information from interestGroup and trustedBiddingSignals.

In this example, we are defining 4 ads based on the color and product:

await navigator.joinAdInterestGroup({
  ads: [
    { renderURL: 'https://dsp.example/red-shirt-ad.html', adRenderId: 'arid1'},
    { renderURL: 'https://dsp.example/blue-shirt-ad.html', adRenderId: 'arid2'},
    { renderURL: 'https://dsp.example/red-pants-ad.html', adRenderId: 'arid3'},
    { renderURL: 'https://dsp.example/blue-pants-ad.html', adRenderId: 'arid4'},
  ],
  trustedBiddingSignalKeys: [
    'userBiddingSignals-someUniqueId',
    // ...and more
  ]
})

Then we send the user's favorite color and product information to be loaded into the Key/Value Service:

fetch('https://dsp.example/kv/load', {
  body: JSON.stringify({
    'userBiddingSignals-someUniqueId': {
      favoriteColor: 'blue',
      favoriteProduct: 'shirt'
    }
  })
})

At a later time, when the auction runs, the trusted bidding signals become available in generateBid(), and that information can be used to reconstruct the URL:

function generateBid(..., trustedBiddingSignals, browserSignals) {
  const { userBiddingSignals } = trustedBiddingSignals
  const { favoriteColor, favoriteProduct } = userBiddingSignals

  return {
    bid: 1,
    render: `https://dsp.example/${favoriteColor}-${favoriteProduct}-ad.html`
  }
}

Return reporting IDs from generateBid()

Since the reporting IDs are not included in the B&A auction payload, the ID becomes available to generateBid() through trusted bidding signals. Once which reporting ID to use is determined, the chosen reporting ID is returned from generateBid(). The returned IDs must match the IDs defined in the interest group.

In this example, ad 1 is chosen, and its associated render ID is returned from generateBid():

generateBid(..., trustedBiddingSignals, ) {
  const { ad1ReportingId, ad2reportingId } = trustedBiddingSignals;
  // ...
  return {
    bid: 1,
    render: 'https://dsp.example/ad-1.html'
    buyerReportingId: ad1reportingId
  }
}

The returned reporting ID becomes available in reportWin() through buyerReportingSignals:

reportWin(..., buyerReportingSignals) {
  const { buyerReportingId } = buyerReportingSignals;
}

If buyerReportingId is not returned from generateBid(), then the interestGroupName value is available in buyerReportingSignals instead of buyerReportingId.

Visit the Reporting ID guide to learn more.

Next steps

The following resources are available for you

Learn more

Have questions?