Most advertisers work with several different ad networks to serve ads across publisher apps. If ad networks register their own attribution sources and triggers with the API, they'll receive self-attributed event and summary reports.
However, advertisers who want to use a third party to perform cross-network attribution (XNA) to determine a single winning ad for a given conversion can continue doing so, using the following techniques:
- Set up an in-house server to register trigger events and receive attribution reports from the API
- Continue using an existing mobile measurement partner
Regardless of the technique an advertiser chooses to use, the Attribution Reporting API supports a number of different features that enable a third party to customize XNA logic on behalf of an advertiser:
- A third party can perform attribution with the API with or without redirects from ad networks.
- Priority, filters, and deduplication keys can provide additional attribution customization based on source and trigger dimensions.
- Post-install attribution windows allow sources that drove an install to continue to win credit for future in-app conversion events.
The attribution model ad techs use for cross-network deduplication and choosing winning sources can have varying levels of complexity depending on how these API features are leveraged.
The examples below illustrate example scenarios for how these features could be used, and how different configurations affect which attribution source ultimately wins credit for a given trigger event.
Process
The following list outlines the steps of the XNA process. For simplicity, the steps listed here assume a model where the advertiser uses a serving ad tech to deliver ads and an MMP for conversion measurement. However, the API design is flexible— functionality does not differ across different types of ad techs, nor does it require the use of an ad tech.
- Source registration: User views or clicks an ad, and the serving ad tech registers these sources with the API. The serving ad tech may also redirect to other ad techs who can register sources directly with the API as well, or enable cross-network attribution without redirects.
- Trigger registration: User performs a conversion associated action, such as first app open, purchase, or add to cart, upon which an MMP registers a trigger with the API. The MMP may also redirect to other ad techs who can register triggers directly with the API. If an MMP needs to enable cross-network attribution without redirects, the attribution config must be specified during trigger registration.
- Attribution: If attribution config is specified during trigger registration, derived sources are generated on the MMP's behalf. Each trigger is attempted to be matched with either an eligible source registered directly by the MMP or an eligible derived source generated on behalf of the MMP using serving ad tech's sources. The remaining sources, which did not win attribution, are dropped and no longer eligible to win attribution for future conversions. You may also see this referred to as ‘lose once, lose always' in other parts of documentation.
- When a derived source loses attribution, the API will not generate future derived sources based on the original source when future conversion events are registered by the MMP. The serving ad tech and other MMPs may still use the original source for future attribution. This is described in detail in Scenario 6.
- Report generation: Attribution leads to event or aggregate report generation. Please note that only aggregate reports are generated for derived sources.
- Report delivery: Generated reports are scheduled for delivery.
Scenario 1: Cross network attribution with redirects
An advertiser is working with 2 serving ad techs and 1 MMP. When ads served by the serving ad techs are clicked, the serving ad techs redirect to the MMP on source registration. When a user converts in the app, the MMP redirects to the ad techs on trigger registration.
The MMP will receive a cross-network deduped report and each serving ad tech will receive self-attributed reports.
Registrations timeline
At t0, the user clicks on an ad served by ad-tech1 which registers a source Source1 along with its redirect Source2 by mmp-ad-tech:
"Attribution-Reporting-Register-Source": {
"source_event_id": "34532",
"web_destination": "https://destination.example.com",
"priority": "10",
"expiry": "172800",
"aggregation_keys": {
"campaignCounts": "0x1"
}
},
"Attribution-Reporting-Redirect": [
"https://www.mmp-ad-tech.com/source2"
]
// Registered by mmp-ad-tech via redirects
"Attribution-Reporting-Register-Source": {
"source_event_id": "788324",
"web_destination": "https://destination.example.com",
"priority": "30",
"expiry": "172800",
"aggregation_keys": {
"campaignCounts": "0x2",
"geoValue": "0x102"
}
}
At t1, the user clicks on an ad served by ad-tech2 to register Source3 along with its redirect to mmp-ad-tech, i.e. Source4:
"Attribution-Reporting-Register-Source": {
"source_event_id": "6574435",
"web_destination": "https://destination.example.com",
"priority": "10",
"expiry": "172800",
"aggregation_keys": {
"campaignCounts": "0x3"
}
},
"Attribution-Reporting-Redirect": [
"https://www.mmp-ad-tech.com/source"
]
// Registered by mmp-ad-tech via redirects
"Attribution-Reporting-Register-Source": {
"source_event_id": "4532343",
"web_destination": "https://destination.example.com",
"priority": "20",
"expiry": "172800",
"aggregation_keys": {
"campaignCounts": "0x4"
}
}
At t2, the user's action or conversion in the advertiser app results in a trigger registration by the mmp-ad-tech (Trigger1) who also redirects to ad-tech1 (Trigger2) an ad-tech2 (Trigger3):
undefined
Result
mmp-ad-tech registered sources Source2 and Source4 compete in attribution for mmp-ad-tech registered trigger Trigger1. Source2 wins over Source4 due to having higher priority. Trigger2 by ad-tech1 gets attributed to Source1 by ad-tech1 and Trigger3 by ad-tech2 gets attributed to Source3 by ad-tech2.
Competing sources for
Fields |
Source1 |
Source2 |
Source3 |
Source4 |
Source registering ad tech |
ad-tech1 |
mmp-ad-tech |
ad-tech2 |
mmp-ad-tech |
source_event_id |
34532 |
788324 |
6574435 |
4532343 |
destination |
https://destination.example.com |
https://destination.example.com |
https://destination.example.com |
https://destination.example.com |
priority |
10 |
30 |
10 |
20 |
Triggers registered
Attribution result
Trigger1 attributes to Source2, Trigger2 attributes to Source1 and Trigger3 attributes to Source3.
Ignored sources post attribution
Source4 - will not compete for attribution in future.
Event Reports
Report URL: https://www.mmp-ad-tech.com/.well-known/attribution-reporting/report-event-attribution
{
"attribution_destination": "https://destination.example.com",
"scheduled_report_time": "800176400",
"source_event_id": "788324",
"trigger_data": "1",
"source_type": "navigation",
"randomized_trigger_rate": 0.0024263
}
Report URL: https://www.ad-tech1.com/.well-known/attribution-reporting/report-event-attribution
{
"attribution_destination": "https://destination.example.com",
"scheduled_report_time": "800176400",
"source_event_id": "34532",
"trigger_data": "2",
"source_type": "navigation",
"randomized_trigger_rate": 0.0024263
}
Report URL: https://www.ad-tech2.com/.well-known/attribution-reporting/report-event-attribution
{
"attribution_destination": "https://destination.example.com",
"scheduled_report_time": "800176400",
"source_event_id": "6574435",
"trigger_data": "3",
"source_type": "navigation",
"randomized_trigger_rate": 0.0024263
}
Aggregate Reports
Report URL: https://www.mmp-ad-tech.com/.well-known/attribution-reporting/report-aggregate-attribution
{
"attribution_destination": "https://destination.example.com",
"histograms": [
{
"key": "0x104",
"value": 11
}
]
}
Report URL: https://www.ad-tech1.com/.well-known/attribution-reporting/report-aggregate-attribution
{
"attribution_destination": "https://destination.example.com",
"histograms": [
{
"key": "0x201",
"value": 21
}
]
}
Report URL: https://www.ad-tech2.com/.well-known/attribution-reporting/report-aggregate-attribution
{
"attribution_destination": "https://destination.example.com",
"histograms": [
{
"key": "0x303",
"value": 31
}
]
}
Scenario 2: Cross-network attribution without redirects
An advertiser is working with 2 serving ad techs and 1 MMP. A user clicks on an ad from the first serving ad tech, which redirects to the MMP on source registration. When the user clicks on an ad from the second serving ad tech, the ad tech does not redirect, opting instead to share a subset of their aggregation keys with the MMP in advance.
The user then converts in the app where the MMP registers the trigger, but does not redirect to either ad tech. The non redirecting ad tech wins last touch attribution. Only the MMP will receive a cross-network deduped summary report that includes this conversion.
Registrations timeline
At t0, user clicks on an ad, resulting in a Source1 registration by ad-tech1 and Source2 registration by mmp-ad-tech via redirect from ad-tech1:
"Attribution-Reporting-Register-Source": {
"source_event_id": "234543",
"web_destination": "https://destination.example.com",
"priority": "20",
"expiry": "172801",
"aggregation_keys": {
"campaignCounts": "0x159"
}
},
"Attribution-Reporting-Redirect": [
"http://www.mmp-ad-tech.com"
]
// Registered by mmp-ad-tech via redirect
"Attribution-Reporting-Register-Source": {
"source_event_id": "45453",
"web_destination": "https://destination.example.com",
"priority": "100",
"expiry": "172801",
"aggregation_keys": {
"campaignCounts": "0x159",
"geoValue": "0x5",
}
}
At t1, user clicks on another ad, resulting into Source3 by ad-tech2 which shares aggregation keys:
// Registered by ad-tech2
"Attribution-Reporting-Register-Source": {
"source_event_id": "978",
"web_destination": "https://destination.example.com",
"priority": "20",
"expiry": "172801",
"aggregation_keys": {
"campaignCounts": "0x159",
"geoValue": "0x5"
},
"shared_aggregation_keys": [
"campaignCounts"
]
}
At t2, the user's action/conversion drives trigger registration by mmp-ad-tech, which contains attribution config for ad-tech2:
"Attribution-Reporting-Register-Trigger": {
"event_trigger_data": [
{
"trigger_data": "2",
"priority": "101"
}
],
"aggregatable_trigger_data": [
{
"key_piece": "0x400",
"source_keys": [
"campaignCounts"
],
"x_network_data": {
"key_offset": 10
}
}
],
"aggregatable_values": {
"campaignCounts": 32768
},
"attribution_config": [
{
"source_network": "enrollment-id-ad-tech-2",
"source_priority_range": {
"start": 1,
"end": 1000
},
"priority": "200",
"expiry": "172800"
}
],
"x_network_key_mapping": {
"enrollment-id-ad-tech-2": "0x4"
}
}
Result
Source2 matches enrollment and destination with the trigger so it becomes a competing source for attribution. Additionally, during trigger registration, an attribution config was specified for ad-tech2 and Source3 via ad-tech2 sharing aggregation keys. This enables a derived source, Source3' to be generated as a competing source for attribution.
Competing sources
Fields |
Source2 |
Source3' |
Original source registering ad-tech |
mmp-ad-tech |
ad-tech2 |
source_event_id |
45453 |
978 |
priority |
100 |
200 |
Triggers registered
Trigger1 by mmp-ad-tech.
Attribution result
Trigger1 gets attributed to Source3' because Source3' has higher priority than Source2.
Ignored sources post attribution
Source2
Event reports
None - event reports aren't generated for derived sources.
Aggregate reports
The parent source of Source3', i.e. Source3, shares only campaignCounts
, the key piece for trigger is calculated by:
(key_piece value) | ((x_network_key_mapping entry) << offset)
0x400 | (0x4 << 10) = 0x1400
Finally, the resultant key is generated by OR-ing trigger key (0x1400) with source key (0x159), i.e. 0x1559
Report URL: http://www.mmp-ad-tech.com/.well-known/attribution-reporting/report-aggregate-attribution
{
"attribution_destination": "https://destination.example.com",
"histograms": [
{
"key": "0x1559",
"value": 32768
}
]
}
Scenario 3: MMP registered source and derived source's parent candidate in the same registration chain
An advertiser is working with 2 serving ad techs and 1 MMP. A user clicks on an ad from the first serving ad tech, which does not redirect on source registration, but shares aggregation keys with MMP. The user clicks on an ad from the second serving ad tech, which both redirects to the MMP on source registration and shares aggregation keys with MMP.
Registrations timeline
At t0, the user clicks on an ad served by ad-tech1, which kicks off registration of Source1:
"Attribution-Reporting-Register-Source": {
"source_event_id": "52343",
"web_destination": "https://destination.example.com",
"priority": "20",
"expiry": "172800",
"aggregation_keys": {
"campaignCounts": "0x159",
"geoValue": "0x5"
},
"shared_aggregation_keys": [
"campaignCounts",
"geoValue"
]
}
At t1, registration chain 2, ad-tech2 registers Source2 and redirects to register an MMP source, Source3:
"source_event_id": "234456",
"web_destination": "https://destination.example.com",
"priority": "20",
"expiry": "172801",
"aggregation_keys": {
"campaignCounts": "0x159"
},
"shared_aggregation_keys": [
"campaignCounts"
]
},
"Attribution-Reporting-Redirect": [
"http://www.mmp-ad-tech.com"
]
"Attribution-Reporting-Register-Source": {
"source_event_id": "4234",
"web_destination": "https://destination.example.com",
"priority": "100",
"expiry": "172800",
"aggregation_keys": {
"campaignCounts": "0x159"
}
}
At t2, trigger registration has attribution configured to generate derived sources from ad-tech1 and ad-tech2:
"Attribution-Reporting-Register-Trigger": {
"event_trigger_data": [
{
"trigger_data": "2",
"priority": "101"
}
],
"aggregatable_trigger_data": [
{
"key_piece": "0x400",
"source_keys": [
"campaignCounts"
],
"x_network_data" : {
"key_offset" : 10
}
}
],
"aggregatable_values": {
"campaignCounts": 32768,
"geoValue": 1664
},
"attribution_config": [
{
"source_network": "enrollment-id-ad-tech-1",
"source_priority_range": {
"start": 1,
"end": 1000
},
"priority": "20",
"expiry": "172800"
},
{
"source_network": "enrollment-id-ad-tech-2",
"source_priority_range": {
"start": 1,
"end": 1000
},
"priority": "20",
"expiry": "172800"
}
],
"x_network_key_mapping" : {
"enrollment-id-ad-tech-1" : "0x2",
"enrollment-id-ad-tech-2" : "0x4"
}
}
The result is that the MMP-registered source in the second registration chain wins attribution. The resulting aggregate report resembles the following:
Result
The derived source from Source2 (with "source_event_id": "234456
") does not participate in attribution because the same registration chain also has a mmp-ad-tech registered source.
Competing sources
Fields |
Source1' |
Source3 |
Original source registering ad tech |
ad-tech1 |
mmp-ad-tech |
source_event_id |
52343 |
4234 |
priority |
20 |
100 |
Triggers registered
Trigger1 by mmp-ad-tech.
Attribution result
Trigger1 gets attributed to Source3 because Source3 has higher priority than Source1'.
Ignored sources post attribution
Source1' - Source1 will not be considered to create a derived source for mmp-ad-tech anymore.
Event reports
Report URL: https://www.ad-tech1.com/.well-known/attribution-reporting/report-event-attribution
{
"attribution_destination": "https://destination.example.com",
"scheduled_report_time": "800176400",
"source_event_id": "4234",
"trigger_data": "2",
"source_type": "navigation",
"randomized_trigger_rate": 0.0024263
}
Aggregate reports
Report URL: http://www.mmp-ad-tech.com/.well-known/attribution-reporting/report-aggregate-attribution
{
"report_url": "http://www.mmp-example.com",
"payload": {
"attribution_destination": "https://destination.example.com",
"histograms": [
{
"key": "0x559"
"value": 32768
}
]
}
}
Scenario 4: Cross network attribution without redirects with criteria on source selection
An advertiser is working with 4 serving ad techs and 1 MMP. A user clicks on an ad from 1 serving ad tech and views ads from the other 3. When a user converts in the advertiser's app, the MMP registers a trigger and specifies which serving ad tech registered sources to create derived sources from, based on the following filters:
- priority_range: choose sources having priority within the given range
- expiry: choose sources with expiry later than specified duration
- source_filters: choose sources whose filter_data matches the source_filters specified
- source_not_filters: choose sources whose not_filters match the source_not_filters specified
Once derived sources are generated based on the criteria, they are eligible to participate in attribution.
Registration timeline
At t0, a user click causes ad-tech1 to register a source Source1, which associates source_type as navigation to this registered source:
"Attribution-Reporting-Register-Source": {
"source_event_id": "87456",
"web_destination": "https://destination.example.com",
"priority": "20",
"expiry": "172801",
"filter_data": {
"filter1": [
"does_not_matter"
],
"filter2": [
"non-match"
]
},
"aggregation_keys": {
"campaignCounts": "0x119",
"geoValue": "0x5"
},
"shared_aggregation_keys": [
"campaignCounts",
"geoValue"
]
}
At t1, a user views the ad causing ad-tech2 to register a source Source2, which associates source_type as event to this registered source:
"Attribution-Reporting-Register-Source": {
"source_event_id": "9078",
"web_destination": "https://destination.example.com",
"priority": "2000",
"expiry": "172801",
"filter_data": {
"filter1": [
"does_not_matter"
],
"filter2": [
"match"
]
},
"aggregation_keys": {
"campaignCounts": "0x129",
"geoValue": "0x5"
},
"shared_aggregation_keys": [
"campaignCounts",
"geoValue"
]
}
At t2, a user view causes ad-tech3 to register a source Source3, which associates source_type as event to this registered source:
"Attribution-Reporting-Register-Source": {
"source_event_id": "2413",
"web_destination": "https://destination.example.com",
"priority": "20",
"filter_data": {
"filter1": [
"non-match"
],
"filter2": [
"non-match"
]
},
"aggregation_keys": {
"campaignCounts": "0x159",
"geoValue": "0x5"
},
"shared_aggregation_keys": [
"campaignCounts",
"geoValue"
]
}
At t3, a user view causes ad-tech4 to register a source Source4, which associates source_type as event to this registered source:
"Attribution-Reporting-Register-Source": {
"source_event_id": "7567",
"web_destination": "https://destination.example.com",
"priority": "20",
"filter_data": {
"filter1": [
"match"
],
"filter2": [
"match"
]
},
"aggregation_keys": {
"campaignCounts": "0x169",
"geoValue": "0x5"
},
"shared_aggregation_keys": [
"campaignCounts",
"geoValue"
]
}
At t4, user conversion leads to mmp-ad-tech registering a trigger with attribution config for all other above mentioned ad-techs' registered sources:
"Attribution-Reporting-Register-Trigger": {
"event_trigger_data": [
{
"trigger_data": "2",
"priority": "100"
}
],
"aggregatable_trigger_data": [
{
"key_piece": "0x400",
"source_keys": [
"campaignCounts"
]
}
],
"aggregatable_values": {
"campaignCounts": 32768,
"geoValue": 1664
},
"attribution_config": [
{
"source_network": "enrollment-id-ad-tech-1",
"source_priority_range": {
"start": 1,
"end": 100
},
"source_filters": {
"source_type": [
"event"
]
},
"priority": "100",
"expiry": "172801"
},
{
"source_network": "enrollment-id-ad-tech-2",
"source_priority_range": {
"start": 1,
"end": 1000
},
"source_filters": {
"source_type": [
"navigation"
]
},
"priority": "100",
"expiry": "172801"
},
{
"source_network": "enrollment-id-ad-tech-3",
"source_priority_range": {
"start": 1,
"end": 1000
},
"source_filters": {
"source_type": [
"navigation"
],
"filter1": [
"match"
],
"filter2": [
"match"
]
},
"priority": "50",
"expiry": "172801"
},
{
"source_network": "enrollment-id-ad-tech-4",
"source_priority_range": {
"start": 1,
"end": 1000
},
"source_filters": {
"source_type": [
"navigation"
],
"filter1": [
"match"
],
"filter2": [
"match"
]
},
"priority": "30",
"expiry": "172801"
}
],
"x_network_key_mapping": {
"enrollment-id-ad-tech-1": "0x1",
"enrollment-id-ad-tech-2": "0x2",
"enrollment-id-ad-tech-3": "0x3",
"enrollment-id-ad-tech-4": "0x4"
}
}
Result
The following sources aren't considered to eligible to generate derived sources from due to criteria mismatch:
- Source1 does not satisfy the "
source_type":"event
" filter on ad-tech1's attribution config - Source2 has priority set to 2000, which is out of ad-tech2's priority range filter (1,1000)
- Source3 does not match value for "
filter2
"
Competing sources
Fields |
Source4' |
Original Source registering ad tech |
ad-tech4 |
source_event_id |
7567 |
destination |
https://destination.example.com |
priority |
30 |
expiry |
Registration time + 2 days |
Triggers registered
Trigger1 by mmp-ad-tech.
Attribution result
Trigger1 gets attributed to Source4' because it's only source eligible for attribution
Ignored sources post attribution
None
Event reports
None - event reports aren't generated for a derived source winner
Aggregate reports
Report URL: http://www.mmp-ad-tech.com
{
"attribution_destination": "https://example.com",
"histograms": [
{
"key": "0x56d",
"value": 32768
},
{
"key": "0x5",
"value": 1664
}
]
}
Scenario 5: Post-install attribution
An advertiser is working with 2 serving ad techs and 1 MMP. A user clicks on an ad from the first ad tech and installs the advertiser app. During attribution for post-install conversions, the derived source with install attribution wins over other sources, even if others have higher priorities.
Registration timeline
At t0, user interaction causes ad-tech1 to register Source1:
"Attribution-Reporting-Register-Source": {
"source_event_id": "3645",
"destination": "android-app://com.example.app",
"priority": "20",
"expiry": "172801",
"install_attribution_window": "86400",
"post_install_exclusivity_window": "864000",
"aggregation_keys": {
"campaignCounts": "0x119",
"geoValue": "0x5"
},
"shared_aggregation_keys": [
"campaignCounts",
"geoValue"
]
}
At t1, the user installs the app com.example.app
on their device
At t2, user interaction causes ad-tech2 to register Source2:
"Attribution-Reporting-Register-Source": {
"source_event_id": "345789",
"destination": "android-app://com.example.app",
"priority": "100",
"aggregation_keys": {
"campaignCounts": "0x159",
"geoValue": "0x5"
},
"shared_aggregation_keys": [
"campaignCounts",
"geoValue"
]
}
At t3, a trigger is registered by mmp-ad-tech with attribution configs for ad-tech1 and ad-tech2:
"Attribution-Reporting-Register-Trigger": {
"event_trigger_data": [
{
"trigger_data": "2",
"priority": "100"
}
],
"aggregatable_trigger_data": [
{
"key_piece": "0x400",
"source_keys": [
"campaignCounts"
]
}
],
"aggregatable_values": {
"campaignCounts": 32768,
"geoValue": 1664
},
"attribution_config": [
{
"source_network": "enrollment-id-ad-tech-1",
"priority": "10",
"expiry": "172801",
"post_install_exclusivity_window": "172800"
},
{
"source_network": "enrollment-id-ad-tech-2",
"priority": "20",
"expiry": "172801"
}
],
"x_network_key_mapping": {
"enrollment-id-ad-tech-1": "0x1",
"enrollment-id-ad-tech-2": "0x3"
}
}
Result
Generated derived sources from Source1 and Source2, that is, Source1' and Source2' respectively, which compete for attribution.
Competing sources
Fields |
Source1' |
Source2' |
Original source registering ad tech |
ad-tech1 |
ad-tech2 |
source_event_id |
3645 |
345789 |
destination |
android-app://com.example.app |
android-app://com.example.app |
priority |
10 |
20 |
Drove app installation |
yes |
no |
Triggers registered
Trigger1 by mmp-ad-tech.
Attribution result
Trigger1 gets attributed to Source1' because it drove the destination app installation. Please note that Source2' had a higher priority.
Ignored sources post attribution
Source2' - derived sources from Source2 will not be considered in attribution for any triggers registered by mmp-ad-tech.
Event reports
None - event reports aren't generated for a derived source winner
Aggregate reports
Report URL: http://www.mmp-ad-tech.com/.well-known/attribution-reporting/report-aggregate-attribution
{
"attribution_destination": "android-app://com.example.app",
"histograms": [
{
"key": "0x519",
"value": 32768
},
{
"key": "0x5",
"value": 1664
}
]
}
Scenario 6: Lose once, lose always
If ad-tech1 has a source whose derived source participated in attribution for mmp-ad-tech's trigger and lost attribution, ad-tech1's source is not used to create a derived source for mmp-ad-tech's triggers thereafter. Here's an example timeline:
- At t0, ad-tech1's Source1 is registered with
"priority": "10"
. - At t1, ad-tech2's Source2 is registered with
"priority": "20"
. - At t2, mmp-ad-tech's Trigger1 is registered with ad-tech1 and ad-tech2's attribution configs.
- At t3, attribution for Trigger1 takes place where the derived source from ad-tech2 wins attribution and ad-tech1's source is ignored
- At t4, ad-tech3's Source3 is registered with
"priority": "5"
. - At t5, mmp-ad-tech's Trigger2 is registered with ad-tech1's and ad-tech3's configs.
- At t6, attribution for Trigger2 takes place, where the derived source from Source3 (Source3') wins the attribution
Result explanation
The derived source from ad-tech1's source had lost attribution for Trigger1, so Source1 wasn't used to create a derived source for Trigger2's attribution. Had it not lost before at t3, it would have won over ad-tech3's source due to having a higher priority.