SMS Authentication

SMS auth is currently in closed alpha. If you’d like early access please reach out to [email protected].

Authenticate users with their phone number by sending verification codes via SMS.

How It Works

SMS authentication is a two-step process:

  1. Send the verification code: the user enters their phone number and requests a verification code.
  2. Submit the verification code: the user enters the 6-digit code they receive via SMS to complete authentication.

The SDK will handle phone number validation, code generation, and delivery automatically.

We support all major countries across Europe, Asia, North and South America.

Pricing varies by country and requires specific area codes to be enabled for your account.

Prerequisites

Implementation

SMS auth is not yet supported with pre-built UI components. Please follow this guide to set up custom UI and configure authentication with hooks.

Step 1: Send the SMS verification code

Ensure you pass the phone number including the country code in the format +<country code><phone number> (i.e. `+15553219876).

import { 
function useAuthenticate(mutationArgs?: UseAuthenticateMutationArgs): UseAuthenticateResult

Hook that provides functions and state for authenticating a user using a signer. It includes methods for both synchronous and asynchronous mutations. Useful if building your own UI components and want to control the authentication flow. For authenticate vs authenticateAsync, use authenticate when you want the hook the handle state changes for you, authenticateAsync when you need to wait for the result to finish processing.

This can be complex for magic link or OTP flows: OPT calls authenticate twice, but this should be handled by the signer.

useAuthenticate
} from "@account-kit/react"
const {
const authenticate: UseMutateFunction<User, Error, AuthParams, unknown>
authenticate
} =
function useAuthenticate(mutationArgs?: UseAuthenticateMutationArgs): UseAuthenticateResult

Hook that provides functions and state for authenticating a user using a signer. It includes methods for both synchronous and asynchronous mutations. Useful if building your own UI components and want to control the authentication flow. For authenticate vs authenticateAsync, use authenticate when you want the hook the handle state changes for you, authenticateAsync when you need to wait for the result to finish processing.

This can be complex for magic link or OTP flows: OPT calls authenticate twice, but this should be handled by the signer.

useAuthenticate
()
const
const handleSendCode: (phone: string) => void
handleSendCode
= (
phone: string
phone
: string) => {
const authenticate: (variables: AuthParams, options?: MutateOptions<User, Error, AuthParams, unknown> | undefined) => void
authenticate
(
{
type: "sms"
type
: "sms",
phone: string
phone
: "+123456789" },
{
MutateOptions<User, Error, AuthParams, unknown>.onSuccess?: ((data: User, variables: AuthParams, onMutateResult: unknown, context: MutationFunctionContext) => void) | undefined
onSuccess
: () => {
// onSuccess only fires once the entire flow is done (SMS OTP + optional MFA). // It still runs even if the final step completes in another tab/window. },
MutateOptions<User, Error, AuthParams, unknown>.onError?: ((error: Error, variables: AuthParams, onMutateResult: unknown, context: MutationFunctionContext) => void) | undefined
onError
: (
error: Error
error
) => {
// Handle error }, }, ); };

Step 2: Show OTP input on status change

Use the useSignerStatus hook to show the otp input once the code is sent:

import React from "react";
        import { 
const useSignerStatus: (override?: AlchemyAccountContextProps) => UseSignerStatusResult

Hook to get the signer status, optionally using an override configuration, useful if you’re building your own login.

useSignerStatus
} from "@account-kit/react";
import {
enum AlchemySignerStatus
AlchemySignerStatus
} from "@account-kit/signer";
const
const TrackStatus: () => JSX.Element
TrackStatus
= () => {
const {
const status: AlchemySignerStatus
status
} =
function useSignerStatus(override?: AlchemyAccountContextProps): UseSignerStatusResult

Hook to get the signer status, optionally using an override configuration, useful if you’re building your own login.

useSignerStatus
();
return ( <> {
const status: AlchemySignerStatus
status
===
enum AlchemySignerStatus
AlchemySignerStatus
.
function (enum member) AlchemySignerStatus.AWAITING_SMS_AUTH = "AWAITING_SMS_AUTH"
AWAITING_SMS_AUTH
&& (
<
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>Prompt the user to enter the OTP code received via SMS</
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
)} </> ); };

Step 3: Verify the OTP code

import { 
function useAuthenticate(mutationArgs?: UseAuthenticateMutationArgs): UseAuthenticateResult

Hook that provides functions and state for authenticating a user using a signer. It includes methods for both synchronous and asynchronous mutations. Useful if building your own UI components and want to control the authentication flow. For authenticate vs authenticateAsync, use authenticate when you want the hook the handle state changes for you, authenticateAsync when you need to wait for the result to finish processing.

This can be complex for magic link or OTP flows: OPT calls authenticate twice, but this should be handled by the signer.

useAuthenticate
} from "@account-kit/react";
const {
const authenticate: UseMutateFunction<User, Error, AuthParams, unknown>
authenticate
} =
function useAuthenticate(mutationArgs?: UseAuthenticateMutationArgs): UseAuthenticateResult

Hook that provides functions and state for authenticating a user using a signer. It includes methods for both synchronous and asynchronous mutations. Useful if building your own UI components and want to control the authentication flow. For authenticate vs authenticateAsync, use authenticate when you want the hook the handle state changes for you, authenticateAsync when you need to wait for the result to finish processing.

This can be complex for magic link or OTP flows: OPT calls authenticate twice, but this should be handled by the signer.

useAuthenticate
();
// When the user submits the OTP code const
const handleVerifyCode: (otpCode: string) => void
handleVerifyCode
= (
otpCode: string
otpCode
: string) => {
const authenticate: (variables: AuthParams, options?: MutateOptions<User, Error, AuthParams, unknown> | undefined) => void
authenticate
(
{
type: "otp"
type
: "otp",
otpCode: string
otpCode
,
}, {
MutateOptions<User, Error, AuthParams, unknown>.onSuccess?: ((data: User, variables: AuthParams, onMutateResult: unknown, context: MutationFunctionContext) => void) | undefined
onSuccess
: () => {
// onSuccess only fires once the entire flow is done (SMS OTP). },
MutateOptions<User, Error, AuthParams, unknown>.onError?: ((error: Error, variables: AuthParams, onMutateResult: unknown, context: MutationFunctionContext) => void) | undefined
onError
: (
error: Error
error
) => {
// Handle invalid code error }, }, ); };

Step 4: Check authentication status

Use the useSignerStatus hook we set up before to wait until the user is authenticated:

import { 
const useSignerStatus: (override?: AlchemyAccountContextProps) => UseSignerStatusResult

Hook to get the signer status, optionally using an override configuration, useful if you’re building your own login.

useSignerStatus
} from "@account-kit/react";
// Inside your component const {
const isConnected: boolean
isConnected
} =
function useSignerStatus(override?: AlchemyAccountContextProps): UseSignerStatusResult

Hook to get the signer status, optionally using an override configuration, useful if you’re building your own login.

useSignerStatus
();
// You can use isConnected to conditionally render UI

Next Steps