Sending SMS
Wire phone-related events to Twilio or MessageBird.
Same model as Sending Emails — the library generates the code, your listener delivers it. SMS is just a different transport.
Events that emit SMS
| Event | When | Has |
|---|---|---|
PhoneVerificationRequestedEvent | User verifies phone | user, code |
PasswordlessCodeRequestedEvent (channel: 'sms') | Passwordless via SMS | identifier, code |
TwoFactorCodeSentEvent (method: 'sms') | MFA SMS challenge | user, code |
With Twilio
With MessageBird
Phone number format
Use E.164 (+15551234567). The library exports normalizedPhone(phone) — call it on user input before storing or sending. See the normalize-email-phone recipe.
Carrier filtering / international rules
Twilio (and other providers) require advance registration for sending to certain countries. SMS-deliverability is more fragile than email — if your user base is global:
- Use a different sender ID per region.
- Allow users to switch to email OTP if SMS fails.
- Surface delivery failures back through the audit hook.
Don't store messages
Logs of plaintext OTP codes are a breach waiting to happen. Don't console.log(event.code) in production, don't include it in error reports, and confirm your provider isn't archiving the body text long-term.
Testing without sending
Swap the listener for a mock in tests, exactly like the email pattern.
Related
- Sending Emails.
- MFA — how the SMS challenge fits into login.
- normalize-email-phone recipe.