docs/content/docs/infrastructure/services/sms.mdx
The SMS service offers:
The SMS service is included in the @better-auth/infra package:
import { sendSMS, createSMSSender } from "@better-auth/infra";
import { sendSMS } from "@better-auth/infra";
await sendSMS({
to: "+1234567890",
code: "123456",
template: "phone-verification",
});
import { createSMSSender } from "@better-auth/infra";
const smsSender = createSMSSender({
apiKey: process.env.BETTER_AUTH_API_KEY,
apiUrl: process.env.BETTER_AUTH_API_URL,
});
// Send multiple SMS messages
await smsSender.send({
to: "+1234567890",
code: "123456",
template: "two-factor",
});
Sends a verification code for phone number verification.
await sendSMS({
to: "+1234567890",
code: "123456",
template: "phone-verification",
});
Example message:
Your verification code is 123456. It expires in 10 minutes.
Sends a two-factor authentication code.
await sendSMS({
to: "+1234567890",
code: "123456",
template: "two-factor",
});
Example message:
Your two-factor authentication code is 123456. Do not share this code with anyone.
Sends a one-time password for passwordless sign-in.
await sendSMS({
to: "+1234567890",
code: "123456",
template: "sign-in-otp",
});
Example message:
Your sign-in code is 123456. It expires in 10 minutes.
If you don't specify a template, a generic verification message is sent:
await sendSMS({
to: "+1234567890",
code: "123456",
});
Example message:
Your verification code is 123456.
Phone numbers must be in E.164 format:
+[country code][number]
Examples:
+14155551234+447911123456+4915112345678+819012345678Common mistakes:
+ prefix: 14155551234 ❌+1 415 555 1234 ❌+1-415-555-1234 ❌+1 (415) 555-1234 ❌interface SMSConfig {
apiKey?: string; // Your Better Auth Infrastructure API key
apiUrl?: string; // Custom API URL (optional)
}
The SMS service automatically reads from environment variables:
BETTER_AUTH_API_KEY=your_api_key_here
BETTER_AUTH_API_URL=https://api.betterauth.com # Optional
Send a single SMS message.
async function sendSMS(
options: SendSMSOptions,
config?: SMSConfig
): Promise<SendSMSResult>
| Property | Type | Required | Description |
|---|---|---|---|
to | string | Yes | Phone number in E.164 format |
code | string | Yes | The OTP code to send |
template | SMSTemplateId | No | Template to use (defaults to generic) |
Create a reusable SMS sender instance.
const sender = createSMSSender(config?: SMSConfig);
// Use the sender
await sender.send(options: SendSMSOptions);
interface SendSMSResult {
success: boolean;
messageId?: string; // SMS provider message ID
error?: string; // Error message if failed
}
const result = await sendSMS({
to: "+1234567890",
code: "123456",
template: "phone-verification",
});
if (result.success) {
console.log("SMS sent:", result.messageId);
} else {
console.error("Failed to send SMS:", result.error);
}
Common error scenarios:
const result = await sendSMS({
to: "+1234567890",
code: "123456",
});
if (!result.success) {
switch (result.error) {
case "API key not configured":
// Missing BETTER_AUTH_API_KEY
break;
case "Invalid phone number":
// Phone number not in E.164 format
break;
default:
// Other delivery error
console.error("SMS error:", result.error);
}
}
When using the dash() or sentinel() plugins with Better Auth's phone authentication, SMS messages are automatically sent for:
You don't need to call sendSMS() manually for these flows - the plugins handle it automatically.
import { betterAuth } from "better-auth";
import { phoneNumber } from "better-auth/plugins";
import { dash } from "@better-auth/infra";
export const auth = betterAuth({
plugins: [
phoneNumber({
sendOTP: async ({ phoneNumber, code }) => {
// This is handled automatically when dash() is configured
// But you can customize if needed:
await sendSMS({
to: phoneNumber,
code,
template: "phone-verification",
});
},
}),
dash({
apiKey: process.env.BETTER_AUTH_API_KEY,
}),
],
});
| Feature | Starter | Pro | Business | Enterprise |
|---|---|---|---|---|
| Transactional SMS | - | Yes | Yes | Yes |
Transactional SMS is available on Pro plans and above.