Twilio Webhooks

Twilio Webhooks, Verified & Reliable.

Validate X-Twilio-Signature (HMAC-SHA1), add DLQ/replay, and never lose SMS status callbacks or voice events.

Works with webhooks from

Stripe Shopify GitHub Twilio SendGrid

What Twilio actually does

Twilio signs webhook payloads with X-Twilio-Signature using HMAC-SHA1. Unlike most providers, Twilio's signature is computed over the full URL + sorted POST parameters, not just the body.

The algorithm: concatenate your webhook URL with all POST body parameters sorted alphabetically (key+value pairs with no separators), then HMAC-SHA1 with your Auth Token and base64-encode.

Never miss an SMS callback or voice event

Server downtime and deploys shouldn't mean lost delivery receipts.

HMAC-SHA1 Validation

Visual debugging for X-Twilio-Signature with clear mismatch reasons: URL, param sorting, or secret issues.

  • URL + params concatenation
  • Base64 encoding handled
  • Timing-safe comparison

DLQ & Replay

Failed SMS status callbacks and voice events stored with full audit trail. One-click replay to staging or prod.

  • No lost delivery receipts
  • Replay to staging safely
  • Complete event history

Latency & Retry Timeline

See exactly what happened, when. Response times, status codes, retry attempts—all visualized.

  • Per-attempt latency
  • Status code tracking
  • Error details captured

Twilio Quick-Start

Add EventDock to your Twilio webhooks in under 5 minutes

1

Create EventDock endpoint

Select Twilio as the provider and paste your upstream URL (your webhook handler for SMS/voice events).

2

Set your Auth Token

Copy your Twilio Auth Token from the Twilio Console and paste it into EventDock as the signing secret.

3

Configure Twilio webhook URL

In your Twilio Console, update your phone number's webhook URL or TwiML App to point to your EventDock ingest URL.

4

Test & enable alerts

Send a test SMS, view it in EventDock's Stream, and enable alerts for failed deliveries.

Signature verification examples

Validate X-Twilio-Signature correctly (HMAC-SHA1 + URL + sorted params)

Node.js verification

Twilio's signature is computed over: URL + sorted(key1+value1+key2+value2+...) then HMAC-SHA1 and base64 encoded.

const crypto = require('crypto');

function verifyTwilioSignature(url, params, signature, authToken) {
  // Sort params alphabetically and concatenate key+value pairs
  const sortedParams = Object.keys(params)
    .sort()
    .map(key => key + params[key])
    .join('');

  // Signature input: URL + sorted params (no separators)
  const signatureInput = url + sortedParams;

  // Compute HMAC-SHA1 and base64 encode
  const expectedSignature = crypto
    .createHmac('sha1', authToken)
    .update(signatureInput)
    .digest('base64');

  // Timing-safe comparison
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// Usage in Express
app.post('/twilio-webhook', express.urlencoded({ extended: false }), (req, res) => {
  const signature = req.headers['x-twilio-signature'];
  const fullUrl = 'https://your-domain.com/twilio-webhook';
  const authToken = process.env.TWILIO_AUTH_TOKEN;

  if (!verifyTwilioSignature(fullUrl, req.body, signature, authToken)) {
    return res.status(401).send('Unauthorized');
  }

  // Process webhook...
  res.status(200).send('OK');
});

Python verification

import hmac
import hashlib
import base64

def verify_twilio_signature(url: str, params: dict, signature: str, auth_token: str) -> bool:
    """Verify Twilio X-Twilio-Signature (HMAC-SHA1, base64)."""
    # Sort params alphabetically and concatenate key+value pairs
    sorted_params = ''.join(
        key + params[key]
        for key in sorted(params.keys())
    )

    # Signature input: URL + sorted params
    signature_input = url + sorted_params

    # Compute HMAC-SHA1 and base64 encode
    expected = base64.b64encode(
        hmac.new(
            auth_token.encode('utf-8'),
            signature_input.encode('utf-8'),
            hashlib.sha1
        ).digest()
    ).decode('utf-8')

    # Constant-time comparison
    return hmac.compare_digest(expected, signature)

# Usage in Flask
@app.route('/twilio-webhook', methods=['POST'])
def twilio_webhook():
    signature = request.headers.get('X-Twilio-Signature')
    full_url = request.url
    auth_token = os.environ['TWILIO_AUTH_TOKEN']

    if not verify_twilio_signature(full_url, request.form.to_dict(), signature, auth_token):
        return 'Unauthorized', 401

    # Process webhook...
    return 'OK', 200

Frequently Asked Questions

How does Twilio signature verification work?

Twilio uses HMAC-SHA1 for signature verification. The signature is computed over the full webhook URL concatenated with all POST parameters sorted alphabetically (key+value pairs with no separators). The result is base64-encoded and sent in the X-Twilio-Signature header.

What if my webhook handler is down when an SMS status callback arrives?

EventDock buffers the webhook and retries with exponential backoff. Failed events go to DLQ where you can replay them once your service is back online. No lost delivery receipts or status updates.

Does EventDock modify Twilio webhook headers?

No. EventDock forwards original Twilio headers unchanged. We add X-EventDock-* metadata headers for tracing but preserve all original request headers and body.

Why does Twilio use HMAC-SHA1 instead of SHA-256?

Twilio's signature algorithm predates the widespread adoption of SHA-256 for webhooks. While SHA-1 has known weaknesses for collision attacks, HMAC-SHA1 remains secure for message authentication when used with a secret key.

How do I get my Twilio Auth Token for signature verification?

Your Auth Token is available in the Twilio Console under Account > API Keys and Tokens. This is the secret used to verify X-Twilio-Signature. Keep it secure and never expose it in client-side code.

Why is my signature verification failing?

Common causes: (1) URL mismatch - the URL in your verification must match exactly what Twilio sends to, including protocol and trailing slashes. (2) Parameter sorting - parameters must be sorted alphabetically by key name. (3) URL encoding - decoded values should be used. (4) Using SHA-256 instead of SHA-1.

Start free — never miss a Twilio callback

5,000 events/month free. No credit card required. 5-minute setup.

Start Free Trial

Other providers: Stripe · Shopify · GitHub