
Agentic Salesforce to Snowflake ELT: From One Prompt to a Governed Pipeline
How Bruin CLI, Bruin MCP, Bruin Cloud, and agent skills can build and maintain a Salesforce to Snowflake ELT pipeline across bronze, silver, and gold layers.
A one-time SDK setup for game studios: cross-write Adjust ADID, Firebase user_pseudo_id, and your own user_id so installs join cleanly across both systems. AppsFlyer, Singular, and Branch follow the same pattern.

Sabri Karagonen
Data & Product

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.
Adjust and Firebase run in the same app but generate different IDs:
user_pseudo_id. Anonymous, persistent, and the primary key in BigQuery's events_* table.adid. The primary key on the Adjust side.user_id once 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.
Here are the three SDK calls the studio added in their next iOS and Android release:
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.
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.
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.
Same idea on AppsFlyer, Singular, and Branch, only the method names change.
appsflyer_id via AppsFlyerLib.getAppsFlyerUID(), write to Firebase. Send your customer_user_id with setCustomerUserId() and the Firebase pseudo ID via setAdditionalData().Singular.getSingularDeviceId(), write to Firebase. Send your user_id with Singular.setCustomUserId(). Pass firebase_pseudo_id as a global property.Branch.getInstance().getFirstReferringParams(), write to Firebase. Call setIdentity(userId) on login.If you only remember one thing: every ID your stack generates should be readable from every other system.
"Isn't pushing IDs around a privacy issue?" Not if your consent flow is set up correctly.
user_pseudo_id is anonymous, Google treats it as non-PII.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.
Open the ticket for your next Adjust release and add three SDK calls:
Adjust.onCreate(), fetch the ADID and write it as a Firebase user property.FirebaseAnalytics initializes, fetch the appInstanceId and pass it as an Adjust global callback parameter.user_id to 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.

How Bruin CLI, Bruin MCP, Bruin Cloud, and agent skills can build and maintain a Salesforce to Snowflake ELT pipeline across bronze, silver, and gold layers.

Most AI data analysts live in Slack or a browser. Bruin runs in WhatsApp too. Here is why field, sales, and ops teams prefer asking their data questions there, what it takes to make it actually work, and how to roll it out safely.
Can you just use ChatGPT, Claude, or a coding agent like Codex to analyze your company data? Here is the honest difference between a general AI model and a purpose-built AI data analyst, why a model alone is not enough, and what it takes to get trustworthy answers from live company data.