pealdevDOCS
Back to peal.dev

AUTH STARTER · PHONE NUMBER OTP

Phone number OTP

UI and DB fields are wired. The actual SMS sending is up to you — Better Auth doesn't ship an SMS provider.

Phone number auth is interesting because Better Auth generates the OTP code, but DELIVERY is your problem. There's no built-in SMS — you plug in Twilio, Vonage, AWS SNS, or whoever, in src/lib/auth.ts.

Out of the box, phone OTP sends nothing

The sendOTP function in src/lib/auth.ts is a stub that console.warns the code in dev. In production it silently does nothing. If you ship phone OTP without wiring an SMS provider, your users WILL get stuck on a code-entry screen forever. Replace the stub.

Turn it on

TypeScript
// src/auth.config.ts
plugins: {
  phoneNumber: { enabled: true },
},

Wire your SMS provider (Twilio example)

TypeScript
// src/lib/auth.ts — replace the stub
import twilio from "twilio";
const sms = twilio(env.TWILIO_ACCOUNT_SID, env.TWILIO_AUTH_TOKEN);

phoneNumber({
  sendOTP: async ({ phoneNumber: phone, code }) => {
    await sms.messages.create({
      body: `Your ${authConfig.appName} code: ${code}`,
      from: env.TWILIO_FROM_NUMBER,
      to: phone,
    });
  },
}),

Client flow

TypeScript
// Request OTP
await authClient.phoneNumber.sendOtp({ phoneNumber: "+14155550123" });

// Verify
await authClient.phoneNumber.verify({
  phoneNumber: "+14155550123",
  code,
});

E.164 format only

Phone numbers must be in E.164 (+14155550123, no spaces, no dashes). The UI component already enforces it — if you build your own form, validate before sending.

SMS costs real money

Twilio is roughly $0.0079 per SMS to US numbers, more internationally. Magic links and email OTP are effectively free at small scale. Use SMS as a last resort, not a default.

Linking a phone after sign-up

The dashboard component src/components/auth/phone-link.tsx lets logged-in users link or change their phone. Drop it into your settings page.