How to Integrate Twilio SMS in Any Web App
·APIScout Team
twiliosms apimessagingtutorialapi integration
How to Integrate Twilio SMS in Any Web App
Twilio is the most widely used SMS API. This guide covers everything: sending messages, receiving replies, phone number verification, and handling delivery reports.
What You'll Build
- Send SMS messages programmatically
- Receive and respond to incoming SMS
- Phone number verification (OTP)
- Delivery status tracking via webhooks
- MMS (picture messages)
Prerequisites: Node.js 18+, Twilio account (free trial includes $15 credit).
1. Setup
Create a Twilio Account
- Sign up at twilio.com
- Get a phone number (free with trial)
- Copy your Account SID, Auth Token, and phone number
Install
npm install twilio
Initialize
// lib/twilio.ts
import twilio from 'twilio';
export const twilioClient = twilio(
process.env.TWILIO_ACCOUNT_SID!,
process.env.TWILIO_AUTH_TOKEN!
);
export const TWILIO_PHONE = process.env.TWILIO_PHONE_NUMBER!;
Environment Variables
# .env.local
TWILIO_ACCOUNT_SID=AC...
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_PHONE_NUMBER=+15551234567
2. Send SMS
Basic Message
import { twilioClient, TWILIO_PHONE } from '@/lib/twilio';
const message = await twilioClient.messages.create({
body: 'Your order #1234 has shipped! Track it here: https://track.example.com/1234',
from: TWILIO_PHONE,
to: '+15559876543',
});
console.log('Message SID:', message.sid);
console.log('Status:', message.status); // 'queued'
API Route (Next.js)
// app/api/sms/send/route.ts
import { NextResponse } from 'next/server';
import { twilioClient, TWILIO_PHONE } from '@/lib/twilio';
export async function POST(req: Request) {
const { to, message } = await req.json();
// Validate phone number format
if (!/^\+\d{10,15}$/.test(to)) {
return NextResponse.json(
{ error: 'Invalid phone number format. Use E.164: +15551234567' },
{ status: 400 }
);
}
try {
const result = await twilioClient.messages.create({
body: message,
from: TWILIO_PHONE,
to,
});
return NextResponse.json({
sid: result.sid,
status: result.status,
});
} catch (error: any) {
return NextResponse.json(
{ error: error.message },
{ status: error.status || 500 }
);
}
}
3. Receive SMS
Set Up Webhook
In Twilio Console → Phone Numbers → Your Number → Messaging:
- When a message comes in:
https://your-server.com/api/sms/receive - HTTP Method: POST
Handle Incoming Messages
// app/api/sms/receive/route.ts
import { NextResponse } from 'next/server';
import twilio from 'twilio';
export async function POST(req: Request) {
const formData = await req.formData();
const from = formData.get('From') as string;
const body = formData.get('Body') as string;
const messageSid = formData.get('MessageSid') as string;
console.log(`SMS from ${from}: ${body}`);
// Process the message (save to DB, trigger action, etc.)
await processIncomingSMS({ from, body, messageSid });
// Reply with TwiML
const twiml = new twilio.twiml.MessagingResponse();
twiml.message('Thanks for your message! We\'ll get back to you shortly.');
return new Response(twiml.toString(), {
headers: { 'Content-Type': 'text/xml' },
});
}
Auto-Responder
const twiml = new twilio.twiml.MessagingResponse();
const lowerBody = body.toLowerCase().trim();
if (lowerBody === 'status') {
twiml.message('Your order is on its way! ETA: Tomorrow by 5pm.');
} else if (lowerBody === 'help') {
twiml.message('Commands:\nSTATUS - Check order\nSTOP - Unsubscribe\nHELP - This menu');
} else if (lowerBody === 'stop') {
twiml.message('You have been unsubscribed. Reply START to re-subscribe.');
await unsubscribeUser(from);
} else {
twiml.message('Reply HELP for available commands.');
}
4. Phone Number Verification (OTP)
Using Twilio Verify
npm install twilio # Already installed
Send Verification Code
// app/api/verify/send/route.ts
import { NextResponse } from 'next/server';
import { twilioClient } from '@/lib/twilio';
const VERIFY_SERVICE_SID = process.env.TWILIO_VERIFY_SERVICE_SID!;
export async function POST(req: Request) {
const { phoneNumber } = await req.json();
const verification = await twilioClient.verify.v2
.services(VERIFY_SERVICE_SID)
.verifications.create({
to: phoneNumber,
channel: 'sms', // or 'call', 'email', 'whatsapp'
});
return NextResponse.json({
status: verification.status, // 'pending'
});
}
Check Verification Code
// app/api/verify/check/route.ts
import { NextResponse } from 'next/server';
import { twilioClient } from '@/lib/twilio';
const VERIFY_SERVICE_SID = process.env.TWILIO_VERIFY_SERVICE_SID!;
export async function POST(req: Request) {
const { phoneNumber, code } = await req.json();
const check = await twilioClient.verify.v2
.services(VERIFY_SERVICE_SID)
.verificationChecks.create({
to: phoneNumber,
code,
});
if (check.status === 'approved') {
// Mark phone as verified in your database
await markPhoneVerified(phoneNumber);
return NextResponse.json({ verified: true });
}
return NextResponse.json({ verified: false }, { status: 400 });
}
Verification UI
'use client';
import { useState } from 'react';
export function PhoneVerification() {
const [phone, setPhone] = useState('');
const [code, setCode] = useState('');
const [step, setStep] = useState<'phone' | 'code' | 'verified'>('phone');
const sendCode = async () => {
await fetch('/api/verify/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phoneNumber: phone }),
});
setStep('code');
};
const checkCode = async () => {
const res = await fetch('/api/verify/check', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phoneNumber: phone, code }),
});
const data = await res.json();
if (data.verified) setStep('verified');
};
if (step === 'verified') return <p>✅ Phone verified!</p>;
return (
<div>
{step === 'phone' && (
<>
<input
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="+15551234567"
/>
<button onClick={sendCode}>Send Code</button>
</>
)}
{step === 'code' && (
<>
<input
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="Enter 6-digit code"
maxLength={6}
/>
<button onClick={checkCode}>Verify</button>
</>
)}
</div>
);
}
5. Delivery Status Webhooks
Configure Status Callback
const message = await twilioClient.messages.create({
body: 'Your appointment is tomorrow at 2pm.',
from: TWILIO_PHONE,
to: '+15559876543',
statusCallback: 'https://your-server.com/api/sms/status',
});
Handle Status Updates
// app/api/sms/status/route.ts
export async function POST(req: Request) {
const formData = await req.formData();
const messageSid = formData.get('MessageSid') as string;
const status = formData.get('MessageStatus') as string;
const errorCode = formData.get('ErrorCode') as string;
// Status values: queued → sent → delivered (or failed/undelivered)
await updateMessageStatus(messageSid, status, errorCode);
return new Response('OK');
}
Status Flow
queued → sending → sent → delivered ✅
→ undelivered ❌ (carrier rejected)
→ failed ❌ (Twilio couldn't send)
6. Send MMS (Picture Messages)
const message = await twilioClient.messages.create({
body: 'Check out this product!',
from: TWILIO_PHONE,
to: '+15559876543',
mediaUrl: ['https://your-cdn.com/product-image.jpg'],
});
MMS limitations: Only works in US and Canada. Images must be publicly accessible URLs. Max 5MB per media file.
Pricing
| Message Type | US/Canada | International |
|---|---|---|
| SMS (outbound) | $0.0079 | $0.05-$0.15 |
| SMS (inbound) | $0.0075 | Varies |
| MMS (outbound) | $0.0200 | Not available |
| Phone number | $1.15/month | Varies |
| Verify OTP | $0.05/verification | $0.05+ |
Trial account: $15 free credit. Can only send to verified numbers.
Common Mistakes
| Mistake | Impact | Fix |
|---|---|---|
| Not using E.164 format | Messages fail to send | Always use +[country][number] format |
| Ignoring STOP/unsubscribe | Legal violations (TCPA) | Honor opt-outs automatically |
| No rate limiting | Twilio rate limits you, messages queue | Limit to 1 msg/sec per number |
| Not handling delivery failures | Silent message loss | Use status callbacks |
| Sending from trial to unverified numbers | Messages fail | Upgrade or verify recipient numbers |
| Exposing Auth Token | Account compromise | Server-side only |
Building with SMS? Compare Twilio vs Vonage vs Telnyx on APIScout — pricing, global coverage, and developer experience.