Prerequisites
Before this: create and initialize a Dynamic client (see Creating a Dynamic Client, Initializing the Dynamic Client). Enable Email and/or SMS as sign-in methods in your Dynamic dashboard.How OTP sign-in works
The flow is always two steps, which map directly onto two UI states:- Request a code — collect the user’s email or phone number and call
sendEmailOTP/sendSmsOTP. This returns anOTPVerificationobject (it holds averificationUUIDthat ties the code to this attempt). Show the code-entry screen. - Verify the code — collect the code the user received and call
verifyOTPwith theOTPVerificationfrom step 1. On success the user is authenticated and the SDK stores the session.
verifyOTP handles both sign-in and adding a verified email/phone to an already–signed-in user. It picks the right path from the current session — you call the same function either way.Email OTP
- TypeScript
- React
Drive two screens from the
OTPVerification returned by sendEmailOTP: an email form, then a code-entry form. Here they’re wired to plain DOM elements.SMS OTP
SMS is the same two-step flow, plus a country code.sendSmsOTP takes an ISO country code ('US', 'GB', 'BR', …) and the phone number without the country dial-in prefix. The SDK exports supportedCountries so you can build a picker instead of hard-coding a list.
- TypeScript
- React
Build the country picker from
supportedCountries rather than hard-coding a list, then send and verify exactly as with email.Resend and expiry
Codes expire after a few minutes, so give users a way to request a new one. Re-run the same send call — it returns a freshOTPVerification that replaces the previous one. Add a short cooldown so users can’t spam the button (and to stay within the rate limits below).
Handling errors
The send (sendEmailOTP / sendSmsOTP) and verify (verifyOTP) calls reject with an APIError (from @dynamic-labs-sdk/client/core) when the backend rejects the request. Don’t treat every rejection the same — the response you show depends on which case it is:
Branch on the error so a wrong code keeps the user on the code screen, while a rate limit tells them to wait:
After verification
OnceverifyOTP resolves, the user is authenticated. In React, useUser() now returns the user; in TypeScript, read the session with client.user. New users often need onboarding, MFA enrollment, or an embedded wallet before entering your app — handle that next in Post-login user setup.
See also
- Authenticate with email — email method reference
- Authenticate with SMS — SMS method reference
- Social sign-in & OAuth redirect handling — add social login alongside OTP
- Post-login user setup — what to do once the user is authenticated