Consent · engineers · PMs
ATT is a prompt. IDFA is the identifier.
Reading IDFA without ATT is how App Store review goes. Sending IDFA after deny is how matching looks lucky and legal looks unlucky. Send SKAN and first-party events instead.
Prompt versus identifier
ATTrackingManager asks whether the app may track the user across other companies' apps and sites. Authorized, you may read the IDFA. Denied, restricted, or not determined, you must not use the advertising identifier as if you had it. The value you will see is zeros, or you will not read it.
Ask when you can explain the value. A prompt on first paint converts poorly and poisons the remaining session. MMPs still want the answer, not a delayed maybe. Showing the prompt is not GDPR and not a CMP. A user who says ask app not to track can still be a logged-in purchaser. Those two consents are not substitutes.
Zeros means denied
00000000-0000-0000-0000-000000000000 is not a device. Treat it as missing. Do not persist last week's IDFA after deny. Do not put IDFV in the idfa field to avoid an empty parameter. Empty is honest. The wrong UUID is a join to a device that does not exist in the ads graph.
Adjust S2S still wants a device id: idfa, gps_adid, or another documented id (vendor.adjust.device_id_required). After deny, send a documented alternative, not zeros pretending to be IDFA, and not a SHA-256 of the IDFA. AppsFlyer and Branch have their own identity fields. Put the advertising id only in the advertising id field, and only when ATT authorized it.
https://s2s.adjust.com/event?s2s=1&event_token=f0ob4r&app_token=4w565xzmb54d&idfa=00000000-0000-0000-0000-000000000000
Do not hash IDFA like email
Hash emails when the MMP field is documented as hashed (AppsFlyer email_hashed, Meta em). Leave device advertising ids in the clear unless that vendor's field is documented as hashed. A SHA-256 IDFA will not match Apple's id or the MMP's device graph. Pixellint flags hashed IP on Adjust, AppsFlyer, and Branch. It does not invent a hashed-IDFA rule for a field the vendor wanted raw.
If a vendor later documents a hashed_idfa field, use that field, not the idfa field. Until then, raw UUID or omit. Filling idfa with a digest is the same class of bug as hashing ip_address: a populated column that matches nobody.
What you send instead
IDFV for vendor-scoped analytics, hashed email or phone if the user signed in and you have a basis, SKAN conversion values, and CAPI with action_source=app. Do not copy a web _fbp into an iOS event and call it ATT compliance. Do not put a fake IDFA in the idfa field to avoid an empty parameter.
advertiser_tracking_enabled, att_status, or the vendor's equivalent flag has to match what iOS actually returned. Lying that tracking is enabled so the graph looks pre-ATT will get you policy pain and junk matching. Put the real status on the MMP S2S event. Let SKAN carry what Apple will still attribute.
{
"data": [{
"event_name": "Purchase",
"event_time": 1770000000,
"action_source": "app",
"user_data": {
"em": ["a85e9ca18f34935ab9b0381b25bfad2455444112b0149270fd88e3da172fe196"]
}
}]
}
Android is not ATT
GAID has its own permission story (Play services, advertising id limits). Do not reuse the ATT checklist as a GAID checklist. See IDFA versus IDFV versus GAID. Adjust, AppsFlyer, and Branch still want a device id on S2S, but IDFA is only one of the documented ids.
After a deny, send IDFV, GAID on Android, or the MMP's own device id. Meta app events can still match on hashed email when you have a basis to send it. They cannot invent an IDFA the OS withheld. Filling idfa anyway is not a clever null. Pixellint will not decide ATT status. It will still fail an Adjust event with no device id at all.
https://s2s.adjust.com/event?s2s=1&event_token=f0ob4r&app_token=4w565xzmb54d&gps_adid=660e1d86-6796-463a-be86-897993136018
Check the artifact
Paste the pixel URL or JSON body into the
playground. Same engine as
pixellint validate. Nothing leaves the tab.