Twilio Webhooks: Complete Integration Guide
Learn how to configure and handle Twilio webhooks for SMS, Voice, and WhatsApp integrations. Includes code examples for signature validation, TwiML responses, and best practices for reliable webhook processing.

Twilio Webhooks: Complete Integration Guide
Twilio webhooks notify your application of SMS delivery, incoming calls, WhatsApp messages, and call status changes. This guide covers configuration, security validation, and best practices for reliable integrations.
Understanding Twilio Webhook Types
Each Twilio communication channel has specific webhook events for real-time interaction with your application.
SMS Status Callbacks
When you send an SMS through Twilio, status callbacks inform you about the message lifecycle:
- queued: Message is queued for sending
- sent: Message sent to the carrier
- delivered: Carrier confirmed delivery
- undelivered: Message could not be delivered
- failed: Message failed to send
Use callbacks to track delivery rates, trigger follow-up actions, and maintain communication logs.
Voice Webhooks
Voice webhooks handle both inbound and outbound call events:
- Incoming call webhooks: Triggered when someone calls your Twilio number
- Status callbacks: Report call progress (ringing, in-progress, completed, busy, failed)
- Recording callbacks: Notify when call recordings are ready
- Transcription callbacks: Deliver speech-to-text results
WhatsApp Webhooks
Twilio's WhatsApp Business API uses webhooks for:
- Incoming messages: Text, images, documents, and location shares
- Message status updates: Sent, delivered, read, and failed statuses
- Template message responses: User replies to outbound templates
Configuring Webhook URLs
Setting Up SMS Webhooks
Configure SMS webhooks in the Twilio Console or programmatically when sending messages:
const twilio = require('twilio');
const client = twilio(accountSid, authToken);
const message = await client.messages.create({
body: 'Your order has shipped!',
from: '+15551234567',
to: '+15559876543',
statusCallback: 'https://webhooks.hookmesh.com/v1/twilio/sms-status'
});Setting Up Voice Webhooks
Configure voice webhook URLs on your Twilio phone number:
const number = await client.incomingPhoneNumbers('PNXXXXXXX').update({
voiceUrl: 'https://webhooks.hookmesh.com/v1/twilio/voice-incoming',
voiceMethod: 'POST',
statusCallback: 'https://webhooks.hookmesh.com/v1/twilio/voice-status',
statusCallbackMethod: 'POST'
});WhatsApp Configuration
WhatsApp webhooks are configured through the Twilio Console in the Messaging section, pointing to your webhook endpoint for the WhatsApp-enabled sender.
Request Validation: Securing Your Webhooks
Validate incoming webhooks to ensure they originate from Twilio and weren't tampered with. Twilio signs every request using your Auth Token. See our webhook security best practices guide for comprehensive authentication approaches.
Node.js Signature Validation
Twilio's SDK makes validation straightforward. See verifying webhooks in Node.js for details:
const twilio = require('twilio');
const express = require('express');
const app = express();
// Use raw body for signature validation
app.use('/webhooks/twilio', express.urlencoded({ extended: false }));
const validateTwilioRequest = (req, res, next) => {
const twilioSignature = req.headers['x-twilio-signature'];
const authToken = process.env.TWILIO_AUTH_TOKEN;
const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
const isValid = twilio.validateRequest(
authToken,
twilioSignature,
url,
req.body
);
if (!isValid) {
console.error('Invalid Twilio signature detected');
return res.status(403).send('Forbidden');
}
next();
};
app.post('/webhooks/twilio/sms', validateTwilioRequest, (req, res) => {
const { From, Body, MessageSid } = req.body;
console.log(`Received SMS from ${From}: ${Body}`);
// Process the message asynchronously
processIncomingSms(req.body).catch(console.error);
// Respond immediately
res.type('text/xml').send('<Response></Response>');
});Python Signature Validation
from flask import Flask, request, abort
from twilio.request_validator import RequestValidator
from functools import wraps
import os
app = Flask(__name__)
def validate_twilio_request(f):
@wraps(f)
def decorated(*args, **kwargs):
validator = RequestValidator(os.environ['TWILIO_AUTH_TOKEN'])
url = request.url
post_vars = request.form.to_dict()
signature = request.headers.get('X-Twilio-Signature', '')
if not validator.validate(url, post_vars, signature):
abort(403)
return f(*args, **kwargs)
return decorated
@app.route('/webhooks/twilio/sms', methods=['POST'])
@validate_twilio_request
def handle_sms():
from_number = request.form.get('From')
body = request.form.get('Body')
message_sid = request.form.get('MessageSid')
# Queue for async processing
process_sms_async.delay(from_number, body, message_sid)
return '<Response></Response>', 200, {'Content-Type': 'text/xml'}TwiML Responses for Voice Webhooks
Voice webhooks expect TwiML (Twilio Markup Language) responses that instruct Twilio how to handle the call:
const VoiceResponse = require('twilio').twiml.VoiceResponse;
app.post('/webhooks/twilio/voice', validateTwilioRequest, (req, res) => {
const twiml = new VoiceResponse();
const { From, CallStatus } = req.body;
// Greet the caller
twiml.say({ voice: 'alice' }, 'Welcome to Acme Support.');
// Gather input
const gather = twiml.gather({
numDigits: 1,
action: '/webhooks/twilio/voice/menu',
method: 'POST'
});
gather.say('Press 1 for sales, press 2 for support.');
// Fallback if no input
twiml.say('We didn\'t receive any input. Goodbye.');
res.type('text/xml').send(twiml.toString());
});Common Use Cases
SMS Delivery Receipts
Track message delivery for critical notifications:
app.post('/webhooks/twilio/sms-status', validateTwilioRequest, async (req, res) => {
const { MessageSid, MessageStatus, ErrorCode } = req.body;
await db.messages.update({
where: { twilioSid: MessageSid },
data: {
status: MessageStatus,
errorCode: ErrorCode || null,
updatedAt: new Date()
}
});
if (MessageStatus === 'failed' || MessageStatus === 'undelivered') {
await alerting.notify(`SMS delivery failed: ${MessageSid}`);
}
res.sendStatus(200);
});Incoming Message Handling
Build conversational experiences by responding to incoming messages:
@app.route('/webhooks/twilio/incoming', methods=['POST'])
@validate_twilio_request
def handle_incoming():
body = request.form.get('Body', '').strip().lower()
from_number = request.form.get('From')
response = MessagingResponse()
if body == 'status':
order = get_latest_order(from_number)
response.message(f'Your order #{order.id} is {order.status}')
elif body == 'help':
response.message('Reply STATUS for order updates, STOP to unsubscribe')
else:
response.message('Thanks for your message! Reply HELP for options.')
return str(response), 200, {'Content-Type': 'text/xml'}Call Status Updates
Monitor call center performance with status callbacks:
app.post('/webhooks/twilio/call-status', validateTwilioRequest, async (req, res) => {
const { CallSid, CallStatus, CallDuration, From, To } = req.body;
await analytics.trackCall({
sid: CallSid,
status: CallStatus,
duration: parseInt(CallDuration) || 0,
from: From,
to: To,
timestamp: new Date()
});
res.sendStatus(200);
});Best Practices for Twilio Webhook Integration
1. Always Validate Requests
Never skip signature validation in production. Attackers easily spoof requests without verifying the X-Twilio-Signature header.
2. Respond Quickly
Twilio expects responses within 15 seconds. Voice webhook delays cause awkward silences for callers. Return immediately and process asynchronously:
app.post('/webhooks/twilio/sms', validateTwilioRequest, (req, res) => {
// Respond immediately
res.type('text/xml').send('<Response></Response>');
// Process asynchronously
messageQueue.add('process-sms', req.body);
});3. Handle Retries Gracefully
Twilio retries failed deliveries. Implement idempotent handlers using MessageSid or CallSid to prevent duplicates:
const processedEvents = new Set(); // Use Redis in production
app.post('/webhooks/twilio/status', validateTwilioRequest, async (req, res) => {
const eventKey = `${req.body.MessageSid}-${req.body.MessageStatus}`;
if (processedEvents.has(eventKey)) {
return res.sendStatus(200);
}
processedEvents.add(eventKey);
await processStatusUpdate(req.body);
res.sendStatus(200);
});4. Use HTTPS Endpoints
Twilio requires HTTPS for webhook URLs in production. Ensure your SSL certificates are valid and properly configured.
5. Log Everything
Comprehensive logging helps debug issues and provides an audit trail:
app.post('/webhooks/twilio/*', (req, res, next) => {
console.log({
path: req.path,
timestamp: new Date().toISOString(),
body: req.body,
headers: {
'x-twilio-signature': req.headers['x-twilio-signature']
}
});
next();
});Scaling Twilio Webhooks with Hook Mesh
Hook Mesh handles infrastructure complexity as your communication volume grows:
- Automatic retries: Never lose delivery receipts or incoming messages
- Request queuing: Buffer high-volume traffic during peak messaging
- Signature validation: Built-in verification before forwarding
- Real-time monitoring: Track delivery rates, latency, and error patterns
- Failover routing: Automatically route to backup endpoints if primary is unavailable
Conclusion
Twilio webhooks are fundamental to building responsive communication applications. Whether tracking SMS deliveries, handling calls with TwiML, or managing WhatsApp, proper implementation keeps your application synchronized with real-world events.
Focus on essentials: validate requests, respond quickly, process asynchronously. With Hook Mesh handling delivery logistics, scale from your first to your millionth message confidently. Explore more platforms in our guides on SendGrid email and Slack webhooks, or browse our platform integrations hub.
Related Posts
SendGrid Webhook Events: How to Handle Email Notifications
Learn how to set up and handle SendGrid Event Webhooks for email delivery tracking. Complete guide covering event types, payload structure, signature verification, and best practices with Node.js code examples.
Webhook Security Best Practices: The Complete Guide
Learn how to secure your webhook implementations with HMAC signature verification, replay attack prevention, SSRF mitigation, and more. Includes code examples in Node.js and Python.
Receive and Verify Webhooks in Node.js: A Complete Guide
Learn how to securely receive and verify webhooks in Node.js using Express.js. Covers HMAC signature verification, timestamp validation, replay attack prevention, and common mistakes to avoid.
Webhook Idempotency: Why It Matters and How to Implement It
A comprehensive technical guide to implementing idempotency for webhooks. Learn about idempotency keys, deduplication strategies, and implementation patterns with Node.js and Python code examples.
Webhook Integration Guides: Connect with Any Platform
Comprehensive guides for integrating webhooks from Stripe, GitHub, Shopify, Twilio, and 10+ other platforms. Learn best practices for reliable webhook handling across your entire stack.