This document provides the guidance and the requirements for implementing Prebid Addressability Framework (PAF) on an Ad Server.
For performance purposes, it is recommended to implement the Addressable Content Service directly in the Ad Server. Therefore, the following document is focused on this setup.
Compared to a usual setup with an Ad Server, the Publisher or the Provider of the Ad Server has to implement additional features to enable PAF.
Those features are:
- The generation of the Seeds of the Addressable Contents;
- The emission of Transmissions to the Ad Network;
- The support of the Audit Log for the user;
- The exposition of an Identity endpoint.
The following diagram introduces an overview of this setup:
sequenceDiagram
participant User
participant Publisher
participant AdServer as Ad Server
participant DSP
User->>Publisher: Ask for a webpage
Publisher->>AdServer: Offer Inventory<br />with Prebid SSO Data
AdServer->>AdServer: Generate Seeds for <br /> the Addressable Contents
AdServer->>DSP: Generate and send a <br /> Transmission Request
DSP->>AdServer: Provide an Addressable Contents <br /> and a Transmission Response
AdServer->>Publisher: Provide the Addressable Contents
Publisher->>User: Display the Addressable Contents <br /> and their respective Audit Logs
PAF doesn't standardize the API of the Ad Server for offering inventory because each Ad Server has an existing and specific API. However, to implement PAF, the Ad Server need to implement new features in the existing endpoints called by the Publisher website to offer inventory and get back Addressable Contents.
To understand the steps, it is important to overview how to generate the data and the relationships between them:
- A Publisher offers multiple placements - one for each for Addressable Contents - via an Ad Server.
- An Ad Server generates one Seed for each Addressable Content
- An Ad Server generates and sends one Transaction Request per Seed and Supplier
- A Supplier generates and sends one Transaction Response per Transaction Request
flowchart LR
subgraph AdServer
Seed-- 1-n -->TR[Transmission Request]
end
subgraph Publisher
Placement-- 1-1 -->Seed
end
subgraph Supplier
TR[Transmission Request]-- 1-1 -->TRp[Transmission Response]
end
The Ad Server API has to be extended to require the Prebid SSO cookies of the Publisher website for a given user in addition to the inventory information. Those cookies contain the Prebid SSO Data. As a reminder, Prebid SSO Data are Pseudonymous-Identifiers and Preferences of the user.
flowchart LR
Publisher-- Send Placements info <br /> with Prebid SSO Cookies -->AdServer[Ad Server]
The Ad Server can then deserialize the cookie for having the Prebid SSO Data.
Here is the structure of a Pseudonymous-Identifier:
Field | Type | Details | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
version | Number | The version of PAF used. | ||||||||||||
type | String | The type of Pseudonymous-Identifier. For now, there is only one: "prebid_id". | ||||||||||||
value | String | The Pseudonymous-Identifier value in UTF-8. | ||||||||||||
source | Source object | The Source contains all the data for identifying and trusting the Operator that generated the Pseudonymous-Identifier.
|
Here is the structure of the Preferences:
Field | Type | Details | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
version | Number | The Prebid SSO version used. | ||||||||||||
data | Dictionary | The keys are strings and represent the name of the preferences. The values represent the value of the preference. For now there is only one preference named "optin" and its value is a boolean. |
||||||||||||
source | Source object | The source contains the data for identifying and trusting the CMP that signed lastly the Preferences.
|
The Seed is the association of the Pseudonymous-Identifiers and the Preferences of the user for a given Addressable Content. The Ad Server must generate this association and sign it. Once generated, a Contracting Party - including the Ad Server- in possession of a Seed can share it with another party via Transmissions (see the next steps). A Seed is signed by the Ad Server for audit purposes.
Here is the composition of a Seed:
Field | Type | Details | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
version | Number | The PAF version used. | ||||||||||||
transaction_id | String | A GUID in a String format dedicated to the share of the Prebid SSO data for one Addressable Content. | ||||||||||||
publisher | String | The domain name of the Publisher that displays the Addressable Content | ||||||||||||
source | Source object | The source contains data for identifying and trusting the Publisher.
|
Here is a JSON example of the Seed:
{
"version": 0,
"transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
"publisher": "publisher.com",
"source": {
"domain": "adserver-company.com",
"timestamp": 1639582000,
"signature": "12345_signature"
}
}
The Ad Server must sign the Seeds ("source"."signature"). The Elliptic Curve Digital Signature Algorithm (ECDSA) is used for this purpose. NIST P-256 coupled with the hash algorithm SHA-256 is applied on a built string relying on the Seed data and the Prebid SSO Data.
Here is how to build the UTF-8 string for then generating the signature:
seed.source.domain + '\u2063' +
seed.source.timestamp + '\u2063' +
seed.transaction_id + '\u2063' +
seed.publisher + '\u2063' +
data.identifiers[0].source.signature + '\u2063' +
data.identifiers[1].source.signature + '\u2063' +
... + '\u2063' +
data.identifiers[n].source.signature + '\u2063' +
data.preferences.source.signature
Once the Seeds are generated (one per Addressable Content), the Ad Server shares the Prebid SSO Data via Transmissions with placement data to Contracting Parties. In the case of an existing custom communication (a.k.a not OpenRTB), Transmission Requests must be included in the existing communication and bound structurally or by references to the data of the impressions (also named Addressable Content). One Transmission Requests per Supplier and Seed.
flowchart LR
TR[Transmission Request]-- n-1 ---Supplier
TR-- 1-1 ---Seed
A Transmission Request is composed as followed:
Field | Type | Details | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
version | Number | The PAF version used. | ||||||||||||
seed | Seed object | A Seed object contains all the Prebid SSO Data gathered and signed by the Publisher concerning the user. | ||||||||||||
parents | Array of Transmission Results | A list of Transmission Results that participate to a chain of Transmissions and make this Transmission possible. | ||||||||||||
source | Source object | The source object contains data for identifying the Sender of the Transmission.
|
The Transmission Request list is always associated to Prebid SSO Data that has
been used for generating the Seed. Here is a hypothetical structure of it that
we name data
in the following example:
Field | Type | Details |
---|---|---|
preferences | Preferences object | The Preferences of the user. |
identifiers | Array of Pseudonymous-Identifier objects | The Pseudonymous-Identifiers of the user. For now, it only contains a Prebid ID. |
Similar to the Seed, each Transmission Request contains a signature for audit purposes. It used the data of the Transaction Request and the domain name of the Receiver with the same cryptographic algorithm.
Here is how to build the UTF-8 string:
transmission_request_receiver_domain + '\u2063' +
transmission_request.source.domain + '\u2063' +
transmission_request.source.timestamp + '\u2063' +
seed.source.signature
In the communication, the Transmission Requests must be associated to the Prebid SSO Data. Depending on the existing structure of the communication, it makes sense to have a shared structure for the Prebid SSO Data and multiple Transmissions referring to it.
Here is an example that must be adapted to the existing API of the Ad Server:
{
"data": {
"identifiers": [
{
"version": 0,
"type": "prebid_id",
"value": "7435313e-caee-4889-8ad7-0acd0114ae3c",
"source": {
"domain": "operator0.com",
"timestamp": 1639580000,
"signature": "12345_signature"
}
}
],
"preferences": {
"version": 0,
"data": {
"opt_in": true
},
"source": {
"domain": "cmp1.com",
"timestamp": 1639581000,
"signature": "12345_signature"
}
}
},
"transmissions": [
{
"version": 0,
"seed": {
"version": 0,
"transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
"publisher": "publisher.com",
"source": {
"domain": "publisher.com",
"timestamp": 1639582000,
"signature": "12345_signature"
}
},
"source": {
"domain": "dsp1.com",
"timestamp": 1639581000,
"signature": "12345_signature"
},
"parents": []
},
{
"version": 0,
"seed": {
"version": 0,
"transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
"publisher": "publisher.com",
"source": {
"domain": "publisher.com",
"timestamp": 1639582000,
"signature": "12345_signature"
}
},
"source": {
"domain": "dps1.com",
"timestamp": 1639581000,
"signature": "12345_signature"
},
"parents": []
}
]
}
The Receiver of the Transmission Requests answers back with Transmission Responses. Those Transmission Responses are included in the usual response of an inventory offer. Each Transmission Response doesn't necessary have a Transmission Response. Indeed, only the Transmission Response that participated in the generation of the Addressable Content is required.
A Transmission Response is composed as followed:
Field | Type | Details |
---|---|---|
version | Number | The version of the PAF used. |
transaction_id | String | A GUID dedicated to the Addressable Content. It allows associating the Transmission Responses to Transmission Request |
receiver | String | The domain name of the DSP. |
status | String | Equals "success" if the DSP signed the Transmission and returns it to the sender. Equals "error_bad_request" if the receiver doesn't understand or see inconsistency in the Transmission Request. Equals "error_cannot_process" if the receiver cannot handle the Transmission Request properly. |
details | String | In case of an error status, the DSP can provide details concerning the error. |
children | Array of Transmission Results | An empty array as we consider that the DSP doesn't share the Prebid SSO Data to its suppliers via new transmissions. |
source | Source object | The source contains all the data for identifying the DSP and verifying the Transmission. |
Therefore, here is an example of Transmission Responses that must be adapted to the existing API:
{
"transmissions": [
{
"version": 0,
"transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
"receiver": "dsp1.com",
"status": "success",
"details": "",
"source": {
"domain": "dsp1.com",
"timestamp": 1639589531,
"signature": "12345_signature"
},
"children": []
},
{
"version": 0,
"transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743"8,
"receiver": "dsp1.com",
"status": "success",
"details": "",
"source": {
"domain": "dsp1.com",
"timestamp": 1639589531,
"signature": "12345_signature"
},
"children": []
}
]
}
Once the Ad Server has selected the supplier that will display the Addressable Content, it must generate the Audit Log based on the related Transmission Response and the Prebid SSO Data.
The Audit Log has the following structure:
Field | Type | Detail |
---|---|---|
data | Prebid SSO Data Object | List the Pseudonymous-Identifiers and the Preferences of the user. |
seed | Seed Object | The Seed object is the association of an Addressable Content to the Prebid SSO Data. |
transmissions | List of Transmission Results | A list of Transmission Results |
As described, the Audit Log contains a list of Transmission Results. The Transmission Results are built thanks to the data within the received Transmission Response that participates in the Addressable Content. The required data are the status and the signature of the Transmission Response and its children.
Here is the structure of a Transmission Result:
Field | Type | Details |
---|---|---|
version | Number | The version of the PAF used. |
receiver | String | The domain name of the DSP. |
status | String | Equals "success" if the DSP signed the Transmission and returns it to the sender. Equals "error_bad_request" if the receiver doesn't understand or see inconsistency in the Transmission Request. Equals "error_cannot_process" if the receiver failed to use the data of the Transmission Request properly. |
details | String | In case of an error status, the DSP can provide details concerning the error. |
source | Source object | The source contains all the data for identifying the DSP and verifying the Transmission. |
Let's take an example of a transformation to Transmission Results. Here is a received Transmission Response that helps to generate the Addressable Content:
{
"version": 0,
"transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
"receiver": "ssp1.com",
"status": "success",
"details": "",
"source": {
"domain": "ssp1.com",
"timestamp": 1639589531,
"signature": "12345_signature"
},
"children": [
{
"receiver": "ssp2.com",
"status": "success",
"details": "",
"source": {
"domain": "ssp2.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
},
{
"receiver": "dsp.com",
"status": "success",
"details": "",
"source": {
"domain": "dsp.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
}
]
}
Here is the associated list of Transmission Results:
{
"transmissions": [
{
"version": 0,
"receiver": "ssp1.com",
"status": "success",
"details": "",
"source": {
"domain": "ssp1.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
},
{
"version": 0,
"receiver": "ssp2.com",
"status": "success",
"details": "",
"source": {
"domain": "ssp2.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
},
{
"version": 0,
"receiver": "dsp.com",
"status": "success",
"details": "",
"source": {
"domain": "dsp.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
}
]
}
After this transformation, it is possible to generate the Audit Log. Here is an example:
{
"data": {
"identifiers": [
{
"version": 0,
"type": "prebid_id",
"value": "7435313e-caee-4889-8ad7-0acd0114ae3c",
"source": {
"domain": "operotor0.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
}
],
"preferences": {
"version": 0,
"data": {
"opt_in": true
},
"source": {
"domain": "cmp1.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
}
},
"seed": {
"version": 0,
"transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
"publisher": "publisher.com",
"source": {
"domain": "ad-server.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
},
"transmissions": [
{
"version": 0,
"receiver": "ssp1.com",
"status": "success",
"details": "",
"source": {
"domain": "ssp1.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
},
{
"version": 0,
"receiver": "ssp2.com",
"status": "success",
"details": "",
"source": {
"domain": "ssp2.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
},
{
"version": 0,
"receiver": "dsp.com",
"status": "success",
"details": "",
"source": {
"domain": "dps.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
}
]
}
Finally, the Addressable Content can be displayed to the user on the Publisher page. An Audit Button (ideally per Addressable Content) is available for displaying the Audit UI.
If the used protocol for offering the inventory is OpenRTB, the Ad Server needs to respect the following for integrating PAF.
In step Step 4, the Ad Server must share the Prebid SSO Data in the extensions of the Bid Request:
First, The Transmission Request object in an OpenRTB request keeps the same structure.
It is embedded in the ext
field of each impression. It is
reachable at imp
.ext
.paf
.
Second, the Pseudonymous-Identifiers and the Preferences structures change
in the OpenRTB request to take the advantage of the
Extended Identifiers.
One eid
per Pseudonymous-Identifier (and Preferences).
It is reachable at user
.ext
.eids
.
Comparing to the solution without OpenRTB:
- The Pseudonymous-Identifier value is stored in the
eids
.id
field. - The
eids
.atype
is set to1
because the ID is tied to a specific browser for nom. - The
version
,type
, andsource
fields are gathered in an extension of theeid
:eids
.ext
.paf
. - The Preferences are attached as an extention of the
eid
.
{
"id": "80ce30c53c16e6ede735f123ef6e32361bfc7b22",
"at": 1,
"cur": [ "USD" ],
"imp": [
{
"id": "1",
"bidfloor": 0.03,
"banner": {
"h": 250,
"w": 300,
"pos": 0
},
"ext": {
"paf": {
"version": 0,
"seed": {
"version": 0,
"transaction_id": "a0651946-0f5b-482b-8cfc-eab3644d2743",
"publisher": "publisher.com",
"source": {
"domain": "publisher0.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
},
"parents": []
}
}
}
],
"site": {
"id": "102855",
"cat": [ "IAB3-1" ],
"domain": "www.publisher.com",
"page": "http://www.publisher.com/1234.html ",
"publisher": {
"id": "8953",
"name": "publisher.com",
"cat": ["IAB3-1"],
"domain": "publisher.com"
}
},
"device": {
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2",
"ip": "123.145.167.10"
},
"user": {
"id": "55816b39711f9b5acf3b90e313ed29e51665623f",
"ext":
{
"eids":
[
{
"source": "paf",
"uids": [
{
"atype": 1,
"id": "7435313e-caee-4889-8ad7-0acd0114ae3c",
"ext":
{
"version": 0,
"type": "prebid_id",
"source":
{
"domain": "operotor0.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
}
}
],
"ext": {
"preferences": {
"version": 0,
"data": {
"opt_in": true
},
"source": {
"domain": "cmp1.com",
"timestamp": 1639589531,
"signature": "12345_signature"
}
}
}
}
]
}
}
}
In step 5, the bidder (named Receiver in PAF Transmission) send back a
OpenRTB Bid Response. Each bid
is associated with a Transaction Response. The
Transaction has the same structure explained in Step 5 and is reachable in
the ext
field of a bid
(full path: seatbid[].bid.ext.paf
).
Here is an example:
{
"id": "1234567890",
"bidid": "abc1123",
"cur": "USD",
"seatbid": [
{
"seat": "512",
"bid": [
{
"id": "1",
"impid": "1",
"price": 9.43,
"nurl": "http://adserver.com/winnotice?impid=102",
"iurl": "http://adserver.com/pathtosampleimage",
"adomain": [ "advertiserdomain.com" ],
"cid": "campaign111",
"crid": "creative112",
"attr": [ 1, 2, 3, 4, 5, 6, 7, 12 ],
"ext": {
"paf": {
"version": 0,
"receiver": "dsp1.com",
"status": "success",
"details": "",
"source": {
"domain": "dsp1.com",
"timestamp": 1639589531,
"signature": "12345_signature"
},
"children": []
}
}
}
]
}
]
}