Skip to main content

Prerequisites

Before this page: create and initialize a Dynamic client (see Creating a Dynamic client and Initializing the Dynamic client), and get the user authenticated with email/SMS OTP, social, or passkeys.

What you’ll build

Authentication proves who the user is. It doesn’t guarantee they’re ready to use your app. Depending on your dashboard configuration, a just-signed-in user may still need to:
  1. Complete required fields — profile information you’ve marked required (name, email, custom fields).
  2. Enroll in MFA — set up a TOTP authenticator app and save recovery codes. See MFA.
  3. Register their device — confirm the device via an emailed link. See Device registration.
  4. Get an embedded wallet — create their embedded wallet if your app uses them.
You’ll build a small setup flow that runs these checks after login, shows UI for whatever is still pending, and lets the user into your app once everything is done.
Not every app needs every step. If you haven’t enabled MFA or device registration in the dashboard, those checks simply return false — build only the steps your app requires.

The setup flow

After login, run a chain of checks. Each check answers “does the user still need to do this?” — if yes, show that step’s UI, wait for the user to finish, then move to the next check.

Detecting what’s pending

Each check is a single call. Compose them into one object so the rest of your UI can ask “what’s left?”:
Compose the checks yourself rather than gating on a single “onboarding complete” call. This keeps you in control of exactly which steps block access — for example, you might let users into the app before an embedded wallet exists and create it lazily later.

Building the flow

Run each step in order. When a step needs the user, render your UI, wait for them to finish, then continue. The show* functions below are your implementation — the SDK owns the logic, you own the screens.
updateUser returns an OTPVerification when a sensitive field (email or phone) changes, because the new value must be verified with a code. For plain profile fields it resolves to undefined. If you collect email/phone here, follow up with the OTP verification flow.

Device registration: completing the redirect

Device registration emails the user a link. When they click it they return to your app with a token in the URL — detect and complete it on every page load, the same way you handle social redirects:

Handling errors

Each step can fail independently. Keep the user on the current step when its call rejects, rather than dropping them back to the start.

Alternative approaches

Gate access without per-step UI

If you don’t need a wizard, just block the app until the user is ready by reusing isSetupComplete():

React to completion from another tab

Listen for userChanged to advance when the user finishes a step elsewhere (e.g. clicking the device-registration email in another tab):

Skip steps with your own rules

The chain is yours — branch it on your business logic. For example, only require MFA for users who will access sensitive features:

See also

Last modified on July 10, 2026