Back to Blog
·Hook Mesh Team

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: 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