How to Get Full Attribution Coverage Between Adjust and Firebase
A game studio I worked with could only match 60% of their Firebase installs to an Adjust record. Forty percent of new users were ghosts. They had no idea where those users came from.
The fix was three lines of SDK code. Coverage went to 98% within a week of the next app release.
If you wire this up once during your Adjust integration, you never have to think about it again. The rest of this post is what to put in those three lines.
Why the gap exists
Adjust and Firebase run in the same app but generate different IDs:
- Firebase stamps every install with a
user_pseudo_id. Anonymous, persistent, and the primary key in BigQuery'sevents_*table. - Adjust stamps every install with an
adid. The primary key on the Adjust side. - Your backend has its own
user_idonce the user logs in.
If you do not explicitly tell each SDK about the others, you cannot join them later.
Most of the 40% gap was iOS users who declined ATT. Without an IDFA, Adjust falls back to probabilistic matching, and a chunk of those users never get attributed. The same thing happens on Android when users reset their GAID. Those installs end up in Firebase with no campaign attached.
The three mappings
Here are the three SDK calls the studio added in their next iOS and Android release:
1. Send Adjust ADID to Firebase
When the Adjust SDK initializes, grab the adid and write it to Firebase as a user property.
// Android, Kotlin
Adjust.getAdid { adid ->
if (adid != null) {
FirebaseAnalytics.getInstance(context)
.setUserProperty("adjust_adid", adid)
}
}
// iOS, Swift
Adjust.adid { adid in
if let adid = adid {
Analytics.setUserProperty(adid, forName: "adjust_adid")
}
}
Now every Firebase event for that user carries the Adjust ID. In BigQuery, you can join events_* to the Adjust raw export on user_properties.adjust_adid = adid.
2. Send Firebase user_pseudo_id to Adjust
Firebase exposes its anonymous ID through getAppInstanceId(). Send it to Adjust as a session callback parameter or as a partner parameter.
FirebaseAnalytics.getInstance(context).appInstanceId
.addOnSuccessListener { id ->
Adjust.addGlobalCallbackParameter("firebase_pseudo_id", id)
}
This gives you a second join key, useful when the ADID-side write fails or arrives late.
3. Send your own user_id to Adjust on login
The moment a user logs in or creates an account, push your internal user_id to Adjust as a callback parameter, and set it as a Firebase user property at the same time.
Adjust.addGlobalCallbackParameter("user_id", currentUser.id)
FirebaseAnalytics.getInstance(context).setUserId(currentUser.id)
Now your authenticated users are joinable by user_id across your warehouse, CRM, backend events, Firebase, and Adjust.
AppsFlyer, Singular, Branch
Same idea on AppsFlyer, Singular, and Branch, only the method names change.
- AppsFlyer: read
appsflyer_idviaAppsFlyerLib.getAppsFlyerUID(), write to Firebase. Send yourcustomer_user_idwithsetCustomerUserId()and the Firebase pseudo ID viasetAdditionalData(). - Singular: read
Singular.getSingularDeviceId(), write to Firebase. Send youruser_idwithSingular.setCustomUserId(). Passfirebase_pseudo_idas a global property. - Branch: read the install ID from
Branch.getInstance().getFirstReferringParams(), write to Firebase. CallsetIdentity(userId)on login.
If you only remember one thing: every ID your stack generates should be readable from every other system.
What about consent
"Isn't pushing IDs around a privacy issue?" Not if your consent flow is set up correctly.
- IDFA is gated by ATT. If the user declines, you don't have an IDFA to push anywhere.
- Adjust ADID is not a device ID. It's Adjust's own identifier derived from IDFV plus signals. Sharing it with your own Firebase project is fine.
- Firebase
user_pseudo_idis anonymous, Google treats it as non-PII. - Your own
user_id: if it's an email, hash it before sending. If it's a UUID, you're fine.
As long as your privacy policy mentions you use an MMP and analytics provider, and you respect ATT and GDPR, this is exactly what those vendors expect. If you're in the EU and want to be safe, run it past your DPO.
What to do Monday
Open the ticket for your next Adjust release and add three SDK calls:
- After
Adjust.onCreate(), fetch the ADID and write it as a Firebase user property. - After
FirebaseAnalyticsinitializes, fetch theappInstanceIdand pass it as an Adjust global callback parameter. - In your login handler, push the authenticated
user_idto both SDKs.
Ship, wait two weeks, then count how many Firebase installs have a matching adid in your Adjust raw export. If you were near 60%, you'll be near 95%.
Half a day of SDK work, and you can finally tell where your users came from.
