Integration Best Practices
Most integrations go live seamlessly. But this is a federated ecosystem; dozens of providers, each with their own flows and their own opinions, and a handful of nuances catch teams out. We've collected them here so they don't catch you. This isn't the Integration Guide; that's the how-to. This is the shortlist of gotchas, plus the decisions worth getting right early.
The short version
If you take away nothing else:
- Launch sessions with a top-level redirect. Not a pop-up, not an iframe, not a web view.
- On mobile, use ASWebAuthenticationSession (iOS) or Chrome Custom Tabs (Android).
- Don't keep integration state in
sessionStorage. Users often come back in a different tab. - Set up your custom domain before activating providers. Changing it later can mean re-registering.
- Build your logic against the normalized data model, not one provider's raw output.
- Test on a real phone, against a provider that hands off to a native app. Desktop testing hides most of what goes wrong.
Everything below is the reasoning behind those, plus a few things worth knowing when you get deeper.
Browser and mobile flows
Redirect is the supported path
A full-page redirect to the session URL works the same way for every provider in the network. The alternatives don't: iframes work with about 2% of providers (most rely on first-party cookies, blocked by default in Safari), and web views have inconsistent cookie behavior that breaks the moment a provider hands your user to their banking app. Both look fine in a developer's own browser and fail for real users. Providers using deep links, like California DMV, are the exception: launch those straight from your native app, no browser needed.
Pop-ups are no longer supported
We've removed pop-up support from the Web UI SDK. If you're on a pop-up flow today, plan a move to redirect.
Pop-ups worked well when this ecosystem was simpler. They've degraded for reasons out of our control: providers decide which tab a user returns to and it often isn't the one they left, some providers set headers that sever the pop-up's connection to your page mid-flow, and Safari blocks pop-ups opened after an async call. On top of that, emerging wallet standards (OpenID4VP/HAIP, including EUDI) require the user to stay in a single browser context, which a pop-up can't guarantee.
Rather than have you maintain workarounds for each of these, we've consolidated on the one flow that holds up.
sessionStorage will quietly lose your user
sessionStorage is scoped per tab. When a provider returns your user in a new tab (routine for anything that hands off to a native app) that tab has empty storage and your user arrives unauthenticated, even on a correct redirect flow. This one bites teams who did everything else right. Keep resume state server-side, keyed off your own reference ID, and treat the user's return as a fresh page load. Client-side, localStorage or a session cookie is fine.
Decisions that are hard to reverse
Custom domains, before provider activation
Around 70% of providers involve a redirect. If those should run through your domain rather than ours, configure it first; some government providers tie the registered domain to user-facing branding, so changing it after activation means re-registering with the provider. This is the mistake we see most often.
Direct mode isn't available for every provider
A few providers have flow-level constraints that rule out a direct integration no matter how well you build it. Check the provider's docs page before committing to direct mode in a given market, so you're not discovering it after the UI is built.
Redaction windows and session expiration
Your redaction window needs to outlast your session expiration, or data can be deleted before a session completes. Related: if you redact immediately on success, we'll have nothing left to look at when you bring us a question about that session. Worth deciding where that line sits before go-live rather than during an incident.
Sequencing
Start hosted, embed later
Teams that open with a direct session usually slow themselves down. Prove the journey with a hosted session, then move to direct where you want full UI control. White-labeled products should land on direct sessions. You don't have to move all at once. Capability flags let you tell us which steps your SDK handles today and have us fill the rest, so your web and mobile clients can progress independently.
And use test mode while you build; it includes mock providers covering six different user journeys, so there's no reason to develop against live credentials.
Working with results
Build against the normalized model
Every provider gets mapped into a normalized identity_data object, so a Swedish BankID session and a US mDL arrive in the same shape. That's one response handler instead of 90+.
Check whether a field is guaranteed
Fields are marked always vs. conditionally returned per provider, on each provider's docs page. A field you saw populated in one test session may be conditional, worth confirming rather than assuming.
Provider-specific attributes: useful, but coupling
Some fields only live in the raw provider output: place of birth, card numbers, credential-level details like the certificate serial behind an mDL. Reach for them when you need them, but know that anything built on one is tied to that provider's shape and won't carry over when you add another provider for the same country. Coverage is still expanding, so check the provider page. And be careful with fields that look universal but aren't: a tax ID number in Italy, Mexico, and Bangladesh are three different objects, and only the provider page will tell you that.
Match scores are yours to interpret
Database-match providers return a 0–100 probability score rather than pass/fail. We give you the score; the threshold and matching rules are yours to set.
What we handle for you
Some of this you'd otherwise have to solve yourself:
- Provider protocol churn. DC API implementations differ across Apple Wallet, Google Wallet, and California DMV, and providers change them without warning. Build against our SDK and those changes stay invisible to you.
- Silent provider drift. We run automated contract checking against what providers tell us they return, and get alerted when reality diverges.
- Session fixation protection. Every redirect routes through Trinsic so we can set protective cookies across these cross-device flows. It's a 302 with no visible UI, and there's no bypass.
- Callback URL masking. If your security team flags session IDs in query parameters, a reverse proxy in front of the callback endpoint lets you obfuscate the pattern.
Updated about 13 hours ago
