Twilio API - Comprehensive Communication Platform
When to Use This Skill
Use this skill when working with Twilio's communication APIs for:
SMS/MMS Messaging
- Send and receive text messages programmatically
Voice Communication
- Build voice calling applications with TwiML
Phone Number Management
- Search, purchase, and configure phone numbers
Webhook Integration
- Handle real-time events and delivery notifications with TwiML responses
Two-Way SMS Conversations
- Build interactive SMS experiences
Bulk SMS Sending
- Send messages to multiple recipients with rate limiting
Message Scheduling
- Schedule messages for future delivery
Production Deployment
- Deploy messaging features with error handling and monitoring
A2P 10DLC Registration
- Register brands and campaigns for US A2P messaging compliance
Provider-Agnostic Architecture
- Build systems that support multiple SMS providers (Twilio + Telnyx)
This skill applies to building communication features in applications, setting up SMS notification systems, creating voice IVR systems, or integrating telephony capabilities.
Quick Reference
1. Send Simple SMS (Node.js SDK)
const
twilio
=
require
(
'twilio'
)
;
const
client
=
twilio
(
process
.
env
.
TWILIO_ACCOUNT_SID
,
process
.
env
.
TWILIO_AUTH_TOKEN
)
;
async
function
sendSMS
(
to
,
from
,
body
)
{
const
message
=
await
client
.
messages
.
create
(
{
to
:
to
,
from
:
from
,
body
:
body
}
)
;
return
message
;
}
// Usage
await
sendSMS
(
'+14155552671'
,
'+14155559999'
,
'Hello from Twilio!'
)
;
2. Send SMS with HTTP (No SDK)
const
https
=
require
(
'https'
)
;
function
sendSMS
(
to
,
from
,
body
)
{
const
accountSid
=
process
.
env
.
TWILIO_ACCOUNT_SID
;
const
authToken
=
process
.
env
.
TWILIO_AUTH_TOKEN
;
const
auth
=
Buffer
.
from
(
${
accountSid
}
:
${
authToken
}
)
.
toString
(
'base64'
)
;
const
postData
=
new
URLSearchParams
(
{
To
:
to
,
From
:
from
,
Body
:
body
}
)
.
toString
(
)
;
const
options
=
{
hostname
:
'api.twilio.com'
,
port
:
443
,
path
:
/2010-04-01/Accounts/
${
accountSid
}
/Messages.json
,
method
:
'POST'
,
headers
:
{
'Authorization'
:
Basic
${
auth
}
,
'Content-Type'
:
'application/x-www-form-urlencoded'
,
'Content-Length'
:
postData
.
length
}
}
;
return
new
Promise
(
(
resolve
,
reject
)
=>
{
const
req
=
https
.
request
(
options
,
(
res
)
=>
{
let
data
=
''
;
res
.
on
(
'data'
,
(
chunk
)
=>
{
data
+=
chunk
;
}
)
;
res
.
on
(
'end'
,
(
)
=>
resolve
(
JSON
.
parse
(
data
)
)
)
;
}
)
;
req
.
on
(
'error'
,
reject
)
;
req
.
write
(
postData
)
;
req
.
end
(
)
;
}
)
;
}
3. Validate Phone Numbers (E.164 Format)
function
validateE164
(
phoneNumber
)
{
const
e164Regex
=
/
^
+
[
1
-
9
]
\d
{1,14}
$
/
;
if
(
!
e164Regex
.
test
(
phoneNumber
)
)
{
return
{
valid
:
false
,
error
:
'Phone number must be in E.164 format (e.g., +14155552671)'
}
;
}
return
{
valid
:
true
}
;
}
// Normalize US phone numbers to E.164
function
formatToE164
(
number
)
{
let
digits
=
number
.
replace
(
/
\D
/
g
,
''
)
;
if
(
!
digits
.
startsWith
(
'1'
)
)
{
digits
=
'1'
+
digits
;
}
return
'+'
+
digits
;
}
4. Handle Incoming Messages (Webhook with TwiML)
const
express
=
require
(
'express'
)
;
app
.
use
(
express
.
urlencoded
(
{
extended
:
false
}
)
)
;
app
.
post
(
'/webhooks/twilio'
,
(
req
,
res
)
=>
{
const
from
=
req
.
body
.
From
;
const
body
=
req
.
body
.
Body
;
const
to
=
req
.
body
.
To
;
console
.
log
(
Received: "
${
body
}
" from
${
from
}
)
;
// Respond with TwiML
const
twiml
=
`
;
res
.
set
(
'Content-Type'
,
'text/xml'
)
;
res
.
send
(
twiml
)
;
}
)
;
5. Verify Webhook Signatures (HMAC-SHA1)
const
crypto
=
require
(
'crypto'
)
;
function
verifyTwilioSignature
(
url
,
params
,
signature
,
authToken
)
{
// Build data string from sorted params
const
data
=
Object
.
keys
(
params
)
.
sort
(
)
.
reduce
(
(
acc
,
key
)
=>
acc
+
key
+
params
[
key
]
,
url
)
;
// Generate HMAC-SHA1 signature
const
expectedSignature
=
crypto
.
createHmac
(
'sha1'
,
authToken
)
.
update
(
Buffer
.
from
(
data
,
'utf-8'
)
)
.
digest
(
'base64'
)
;
return
signature
===
expectedSignature
;
}
// Usage in Express with body-parser
app
.
post
(
'/webhooks/twilio'
,
(
req
,
res
)
=>
{
const
signature
=
req
.
headers
[
'x-twilio-signature'
]
;
const
url
=
https://
${
req
.
headers
.
host
}
${
req
.
url
}
;
if
(
!
verifyTwilioSignature
(
url
,
req
.
body
,
signature
,
process
.
env
.
TWILIO_AUTH_TOKEN
)
)
{
return
res
.
status
(
403
)
.
send
(
'Forbidden'
)
;
}
// Process webhook...
const
twiml
=
'<Response></Response>'
;
res
.
set
(
'Content-Type'
,
'text/xml'
)
;
res
.
send
(
twiml
)
;
}
)
;
6. Twilio SDK Signature Validation
const
twilio
=
require
(
'twilio'
)
;
app
.
post
(
'/webhooks/twilio'
,
(
req
,
res
)
=>
{
const
signature
=
req
.
headers
[
'x-twilio-signature'
]
;
const
url
=
https://
${
req
.
headers
.
host
}
${
req
.
url
}
`
;
if
(
!
twilio
.
validateRequest
(
process
.
env
.
TWILIO_AUTH_TOKEN
,
signature
,
url
,
req
.
body
)
)
{
return
res
.
status
(
403
)
.
send
(
'Forbidden'
)
;
}
// Process webhook...
const
twiml
=
new
twilio
.
twiml
.
MessagingResponse
(
)
;
twiml
.
message
(
'Thanks for your message!'
)
;
res
.
set
(
'Content-Type'
,
'text/xml'
)
;
res
.
send
(
twiml
.
toString
(
)
)
;
}
)
;
7. Send with Error Handling and Retry
async
function
sendWithRetry
(
to
,
from
,
body
,
maxRetries
=
3
)
{
for
(
let
attempt
=
1
;
attempt
<=
maxRetries
;
attempt
++
)
{
try
{
return
await
client
.
messages
.
create
(
{
to
,
from
,
body
}
)
;
}
catch
(
error
)
{
if
(
error
.
status
= 500 && attempt < maxRetries ) { // Server error - retry with exponential backoff const delayMs = Math . pow ( 2 , attempt ) * 1000 ; console . log (
Retry ${ attempt } in ${ delayMs } ms...) ; await new Promise ( resolve => setTimeout ( resolve , delayMs ) ) ; } else { throw error ; } } } } 8. Bulk Sending with Rate Limiting async function sendBulkSMS ( recipients , from , body ) { const delayMs = 100 ; // 10 messages/second const results = [ ] ; for ( const recipient of recipients ) { try { const result = await client . messages . create ( { to : recipient , from , body } ) ; results . push ( { success : true , to : recipient , sid : result . sid } ) ; } catch ( error ) { results . push ( { success : false , to : recipient , error : error . message } ) ; } await new Promise ( resolve => setTimeout ( resolve , delayMs ) ) ; } return results ; } 9. Provider-Agnostic Webhook Handler (Twilio + Telnyx) // From Twilio-Aldea production codebase function detectProvider ( payload : any ) : 'twilio' | 'telnyx' { // Telnyx uses JSON with data.event_type if ( payload . data && payload . data . event_type ) { return 'telnyx' ; } // Twilio uses form-urlencoded with MessageSid if ( payload . MessageSid || payload . From ) { return 'twilio' ; } throw new Error ( 'Unknown SMS provider' ) ; } // Unified webhook handler app . post ( '/api/sms/webhook' , async ( req , res ) => { const providerType = detectProvider ( req . body ) ; if ( providerType === 'twilio' ) { // Validate Twilio signature // Return TwiML response const twiml = '' ; res . set ( 'Content-Type' , 'text/xml' ) ; res . send ( twiml ) ; } else { // Validate Telnyx Ed25519 signature // Return JSON response res . status ( 200 ) . json ( { status : 'ok' } ) ; } } ) ; 10. Handle Common Errors function handleTwilioError ( error ) { if ( ! error . status ) { return { type : 'NETWORK_ERROR' , retriable : true } ; } switch ( error . status ) { case 400 : case 422 : // Validation error return { type : 'VALIDATION_ERROR' , message : error . message , code : error . code , retriable : false } ; case 401 : // Check Account SID and Auth Token return { type : 'AUTH_ERROR' , retriable : false } ; case 429 : // Rate limit return { type : 'RATE_LIMIT' , retriable : true , retryAfter : 60 } ; case 500 : case 502 : case 503 : // Server error return { type : 'SERVER_ERROR' , retriable : true } ; default : return { type : 'UNKNOWN_ERROR' , retriable : false } ; } } Key Concepts 1. E.164 Phone Number Format International phone number format: +[country code][number] US Example: +14155552671 UK Example: +442071234567 Always include the + prefix Maximum 15 digits (excluding +) 2. Authentication (Basic Auth) Twilio uses HTTP Basic Authentication with Account SID as username and Auth Token as password: Authorization: Basic base64(ACCOUNT_SID:AUTH_TOKEN) 3. TwiML (Twilio Markup Language) XML-based response format for webhooks:
- <
- Response
- >
- <
- Message
- >
- Your message text here
- </
- Message
- >
- </
- Response
- >
- Common TwiML verbs:
- - Send SMS/MMS reply
- - Redirect to another URL
- - Make voice call
- - Text-to-speech
- - Play audio file
- 4. Webhook Events
- Twilio sends form-urlencoded POST requests with:
- MessageSid
- - Unique message identifier
- From
- - Sender phone number
- To
- - Recipient phone number
- Body
- - Message text
- MessageStatus
- - Message status (queued, sent, delivered, failed, undelivered)
- NumMedia
- - Number of media attachments (MMS)
- 5. Message Status Lifecycle
- queued
- - Message accepted by Twilio
- sending
- - Being sent to carrier
- sent
- - Sent to carrier
- delivered
- - Delivered to recipient (requires StatusCallback)
- undelivered
- - Failed to deliver
- failed
- - Permanent failure
- 6. Signature Validation (HMAC-SHA1)
- Twilio signs webhooks with HMAC-SHA1:
- Concatenate URL + sorted parameters
- Generate HMAC-SHA1 with Auth Token as key
- Base64 encode the result
- Compare with
- X-Twilio-Signature
- header
- 7. A2P 10DLC Registration
- For US messaging, register:
- Brand
- - Your business entity
- Campaign
- - Use case (Customer Care, Marketing, 2FA, etc.)
- Phone Numbers
- - Associate numbers with campaign
- Timeline
-
- 5-7 business days for approval
- 8. Message Encoding and Segmentation
- GSM-7
-
- 160 chars/segment for standard ASCII
- UCS-2
- 70 chars/segment for emoji/unicode
Long messages split into segments (max 10)
Multi-part: GSM-7 = 153 chars/segment, UCS-2 = 67 chars/segment
Production Patterns from Twilio-Aldea
Pattern 1: Provider-Agnostic Webhook Architecture
// Support both Twilio and Telnyx from single endpoint
export
default
async
function
handler
(
req
:
NextApiRequest
,
res
:
NextApiResponse
)
{
const
rawBody
=
await
readRawBody
(
req
)
;
// Auto-detect provider
let
payload
:
any
;
try
{
payload
=
JSON
.
parse
(
rawBody
)
;
// Telnyx
}
catch
{
payload
=
parseFormUrlEncoded
(
rawBody
)
;
// Twilio
}
const
providerType
=
detectProvider
(
payload
)
;
const
provider
=
getProviderByType
(
providerType
)
;
// Validate signature
const
isValid
=
provider
.
validateSignature
(
req
,
rawBody
)
;
if
(
!
isValid
)
{
return
res
.
status
(
403
)
.
json
(
{
error
:
'Invalid signature'
}
)
;
}
// Process message
await
processIncomingSMS
(
payload
,
provider
)
;
// Return provider-specific response
if
(
providerType
===
'twilio'
)
{
res
.
set
(
'Content-Type'
,
'text/xml'
)
;
res
.
send
(
'
' ) ; } else { res . status ( 200 ) . json ( { status : 'ok' } ) ; } } Pattern 2: Raw Body Preservation for Signature Validation // Next.js API route config export const config = { api : { bodyParser : false , // Preserve raw body } , } ; async function readRawBody ( req : NextApiRequest ) : Promise < string { return new Promise < string
( ( resolve , reject ) => { let data = '' ; req . setEncoding ( 'utf8' ) ; req . on ( 'data' , ( chunk ) => { data += chunk ; } ) ; req . on ( 'end' , ( ) => resolve ( data ) ) ; req . on ( 'error' , reject ) ; } ) ; } Pattern 3: Fast Mode vs Compute Mode // Environment variable: SMS_FAST_MODE=true/false const fastMode = process . env . SMS_FAST_MODE ?. toLowerCase ( ) !== 'false' ; if ( fastMode ) { // Return immediate acknowledgment res . status ( 200 ) . send ( twiml ) ; // Process async in background processIncomingSMS ( payload ) . catch ( console . error ) ; } else { // Wait for AI processing await processIncomingSMS ( payload ) ; res . status ( 200 ) . send ( twiml ) ; } Pattern 4: TwiML Response Builder function buildTwiMLResponse ( message ? : string ) : string { if ( ! message ) { return '
' ; } // Escape XML special characters const escaped = message . replace ( / & / g , '&' ) . replace ( / < / g , '<' ) . replace ( / / g , '>' ) . replace ( / " / g , '"' ) . replace ( / ' / g , ''' ) ; return `
( promise : Promise < T
, timeoutMs : number = 25000 ) : Promise < T
{ return Promise . race ( [ promise , new Promise < T
( ( _ , reject ) => setTimeout ( ( ) => reject ( new Error ( 'Timeout' ) ) , timeoutMs ) ) , ] ) ; } // Usage const result = await withTimeout ( processIncomingSMS ( payload ) , 25000 ) ; API Essentials Base URL https://api.twilio.com/2010-04-01 Authentication Authorization: Basic base64(ACCOUNT_SID:AUTH_TOKEN) Environment Variables
.env file
TWILIO_ACCOUNT_SID
- ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- TWILIO_AUTH_TOKEN
- =
- your_auth_token_here
- TWILIO_PHONE_NUMBER
- =
- +18005551234
- Rate Limits
- Messaging: 200 messages per second (enterprise)
- Voice: 100 concurrent calls (default)
- API requests: 10,000 per hour (default)
- Common Response Structure
- {
- "sid"
- :
- "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
- ,
- "date_created"
- :
- "Wed, 18 Aug 2021 20:01:14 +0000"
- ,
- "date_updated"
- :
- "Wed, 18 Aug 2021 20:01:14 +0000"
- ,
- "date_sent"
- :
- null
- ,
- "account_sid"
- :
- "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
- ,
- "to"
- :
- "+14155552671"
- ,
- "from"
- :
- "+14155559999"
- ,
- "body"
- :
- "Hello from Twilio!"
- ,
- "status"
- :
- "queued"
- ,
- "num_segments"
- :
- "1"
- ,
- "num_media"
- :
- "0"
- ,
- "direction"
- :
- "outbound-api"
- ,
- "price"
- :
- null
- ,
- "price_unit"
- :
- "USD"
- ,
- "uri"
- :
- "/2010-04-01/Accounts/ACxxx/Messages/SMxxx.json"
- }
- Quick Start Checklist
- Sign up for Twilio account at
- https://www.twilio.com/try-twilio
- Get Account SID and Auth Token from Console
- Set up environment variables
- Purchase a phone number for testing
- Send your first test SMS (Quick Reference #1)
- Validate phone numbers (Quick Reference #3)
- Set up webhook endpoint (use ngrok for local dev)
- Implement webhook handler with TwiML (Quick Reference #4)
- Add webhook signature verification (Quick Reference #5 or #6)
- Test two-way messaging
- Add error handling with retry logic (Quick Reference #7)
- (US only) Register for A2P 10DLC if sending to US numbers
- Working with This Skill
- For Beginners
- Start Here:
- Use
- Quick Reference #1
- (Send Simple SMS)
- Set up environment variables
- Use
- Quick Reference #3
- (Validate Phone Numbers)
- Test sending to your own phone number
- Set up
- Quick Reference #4
- (Handle Incoming Messages with TwiML)
- Test two-way messaging with ngrok
- Key Concepts to Learn:
- E.164 phone number format
- Basic Authentication (Account SID + Auth Token)
- TwiML XML responses for webhooks
- Message status lifecycle
- Common Beginner Mistakes:
- Forgetting the
- +
- prefix in phone numbers
- Not using E.164 format
- Hardcoding credentials instead of environment variables
- Not returning TwiML from webhook endpoints
- Not validating webhook signatures
- For Intermediate Users
- Focus Areas:
- Implement
- Quick Reference #5 or #6
- (Signature Validation)
- Use
- Quick Reference #7
- (Error Handling with Retry)
- Build conversation flows with state machines
- Implement idempotency (Production Pattern #5)
- Handle StatusCallback webhooks for delivery notifications
- Key Concepts to Master:
- HMAC-SHA1 signature validation
- TwiML advanced features
- Message segmentation and cost optimization
- Error handling patterns
- Rate limiting for bulk sending
- For Advanced Users
- Advanced Patterns:
- Build provider-agnostic handlers (Production Pattern #1)
- Implement timeout protection (Production Pattern #6)
- Design multi-provider architectures
- Optimize with fast mode vs compute mode (Production Pattern #3)
- Build IVR systems with Voice API
- Set up comprehensive monitoring and alerting
- Key Topics:
- Provider-agnostic webhook architecture
- Database-backed idempotency
- Structured logging and monitoring
- A2P 10DLC compliance
- Production deployment patterns
- Common Error Codes
- Authentication Errors
- 20003
- - Authentication failed (check Account SID and Auth Token)
- 20005
- - Account not active
- Validation Errors
- 21211
- - Invalid 'To' phone number
- 21212
- - Invalid 'From' phone number
- 21408
- - Permission to send to this number not enabled
- 21610
- - Attempt to send to unsubscribed recipient
- Rate Limit Errors
- 20429
- - Too many requests (rate limited)
- Message Errors
- 30001
- - Queue overflow (system overloaded)
- 30003
- - Unreachable destination
- 30004
- - Message blocked
- 30005
- - Unknown destination
- 30006
- - Landline or unreachable carrier
- 30007
- - Message filtered (spam)
- 30008
- - Unknown error
- Best Practices
- 1. Always Validate Webhook Signatures
- // Use Twilio SDK for built-in validation
- const
- twilio
- =
- require
- (
- 'twilio'
- )
- ;
- if
- (
- !
- twilio
- .
- validateRequest
- (
- authToken
- ,
- signature
- ,
- url
- ,
- params
- )
- )
- {
- return
- res
- .
- status
- (
- 403
- )
- .
- send
- (
- 'Forbidden'
- )
- ;
- }
- 2. Return TwiML Immediately
- // Don't do expensive processing before responding
- app
- .
- post
- (
- '/webhook'
- ,
- async
- (
- req
- ,
- res
- )
- =>
- {
- // Return TwiML immediately
- res
- .
- set
- (
- 'Content-Type'
- ,
- 'text/xml'
- )
- ;
- res
- .
- send
- (
- '
' - )
- ;
- // Process async
- processMessage
- (
- req
- .
- body
- )
- .
- catch
- (
- console
- .
- error
- )
- ;
- }
- )
- ;
- 3. Use StatusCallback for Delivery Tracking
- await
- client
- .
- messages
- .
- create
- (
- {
- to
- :
- '+14155552671'
- ,
- from
- :
- '+14155559999'
- ,
- body
- :
- 'Hello!'
- ,
- statusCallback
- :
- 'https://yourdomain.com/status'
- }
- )
- ;
- 4. Handle Message Segmentation
- // Keep messages under 160 characters for GSM-7
- function
- optimizeForGSM7
- (
- text
- )
- {
- return
- text
- .
- replace
- (
- /
- [
- ""
- ]
- /
- g
- ,
- '"'
- )
- .
- replace
- (
- /
- [
- ''
- ]
- /
- g
- ,
- "'"
- )
- .
- replace
- (
- /
- [
- —–
- ]
- /
- g
- ,
- '-'
- )
- .
- replace
- (
- /
- …
- /
- g
- ,
- '...'
- )
- ;
- }
- 5. Implement Exponential Backoff
- async
- function
- sendWithBackoff
- (
- to
- ,
- from
- ,
- body
- ,
- maxRetries
- =
- 3
- )
- {
- for
- (
- let
- attempt
- =
- 1
- ;
- attempt
- <=
- maxRetries
- ;
- attempt
- ++
- )
- {
- try
- {
- return
- await
- client
- .
- messages
- .
- create
- (
- {
- to
- ,
- from
- ,
- body
- }
- )
- ;
- }
- catch
- (
- error
- )
- {
- if
- (
- attempt
- <
- maxRetries
- &&
- error
- .
- status
- >=
- 500
- )
- {
- await
- new
- Promise
- (
- r
- =>
- setTimeout
- (
- r
- ,
- Math
- .
- pow
- (
- 2
- ,
- attempt
- )
- *
- 1000
- )
- )
- ;
- }
- else
- {
- throw
- error
- ;
- }
- }
- }
- }
- Resources
- Twilio Console
- :
- https://console.twilio.com/
- API Reference
- :
- https://www.twilio.com/docs/api
- Helper Libraries
- Node.js, Python, PHP, Ruby, C#, Java Status Page : https://status.twilio.com/ Support : https://support.twilio.com/ Version Notes This skill includes: Official Twilio API patterns and best practices Production code examples from Twilio-Aldea SMS platform Provider-agnostic webhook architecture TwiML response patterns Complete signature validation examples TypeScript and JavaScript examples