A comprehensive Node.js SDK for the Twilio communications platform, enabling SMS, voice, video, and other communication services integration.
—
Webhook validation utilities provide security functions to verify that incoming HTTP requests to your application originated from Twilio servers. This prevents unauthorized access and ensures request integrity through cryptographic signature validation.
Core functions for validating Twilio webhook request signatures using HMAC-SHA1 cryptographic verification.
/**
* Validate that an incoming request originated from Twilio
* @param authToken - Your Twilio auth token from the console
* @param twilioSignature - Value of X-Twilio-Signature header from the request
* @param url - The full webhook URL you configured with Twilio (including query string)
* @param params - The POST parameters sent with the request
* @returns true if request is valid, false otherwise
*/
function validateRequest(
authToken: string,
twilioSignature: string,
url: string,
params: Record<string, any>
): boolean;
/**
* Validate request signature including body hash verification
* @param authToken - Your Twilio auth token
* @param twilioSignature - Value of X-Twilio-Signature header
* @param url - The full webhook URL (must include bodySHA256 parameter)
* @param body - The raw request body as a string
* @returns true if both signature and body hash are valid
*/
function validateRequestWithBody(
authToken: string,
twilioSignature: string,
url: string,
body: string
): boolean;
/**
* Validate incoming request using a request object (Express.js compatible)
* @param request - HTTP request object with headers, body, and URL properties
* @param authToken - Your Twilio auth token
* @param opts - Optional configuration for URL construction
* @returns true if request is valid
*/
function validateIncomingRequest(
request: Request,
authToken: string,
opts?: RequestValidatorOptions
): boolean;
/**
* Express.js specific request validation (alias for validateIncomingRequest)
* @param request - Express request object
* @param authToken - Your Twilio auth token
* @param opts - Optional validation configuration
* @returns true if request is valid
*/
function validateExpressRequest(
request: Request,
authToken: string,
opts?: RequestValidatorOptions
): boolean;Usage Examples:
import TwilioSDK from "twilio";
// Access webhook validation functions
const { validateRequest, validateRequestWithBody } = TwilioSDK;
// Basic webhook validation
app.post("/webhook", (req, res) => {
const signature = req.headers["x-twilio-signature"];
const url = "https://myapp.com/webhook";
const params = req.body;
const isValid = validateRequest(
process.env.TWILIO_AUTH_TOKEN,
signature,
url,
params
);
if (!isValid) {
return res.status(403).send("Invalid signature");
}
// Process webhook
res.send("OK");
});
// Validation with body hash (for high-security webhooks)
app.post("/secure-webhook", (req, res) => {
const signature = req.headers["x-twilio-signature"];
const url = `https://myapp.com/secure-webhook?bodySHA256=${req.query.bodySHA256}`;
const body = req.rawBody;
const isValid = validateRequestWithBody(
process.env.TWILIO_AUTH_TOKEN,
signature,
url,
body
);
if (isValid) {
// Process secure webhook
res.send("OK");
} else {
res.status(403).send("Invalid signature or body");
}
});Pre-built Express.js middleware for automatic webhook validation with comprehensive error handling.
/**
* Express.js middleware for Twilio webhook validation and TwiML response helpers
* @param opts - Configuration options or auth token string
* @param authToken - Alternative way to provide auth token
* @returns Express middleware function
*/
function webhook(
opts?: string | WebhookOptions,
authToken?: string | WebhookOptions
): (req: any, res: any, next: Function) => void;
interface WebhookOptions {
/** Whether to validate the request (default: true) */
validate?: boolean;
/** Enable TwiML response helpers (default: true) */
includeHelpers?: boolean;
/** Full webhook URL (overrides host/protocol) */
url?: string;
/** Manually specify hostname for validation */
host?: string;
/** Manually specify protocol for validation */
protocol?: string;
/** Auth token for validation */
authToken?: string;
}Usage Examples:
import express from "express";
import TwilioSDK from "twilio";
const app = express();
const { webhook } = TwilioSDK;
// Basic webhook middleware with environment variable auth token
app.use("/webhooks", webhook());
// Webhook middleware with explicit auth token
app.use("/webhooks", webhook(process.env.TWILIO_AUTH_TOKEN));
// Webhook middleware with custom configuration
app.use("/webhooks", webhook({
validate: true,
host: "myapp.ngrok.io",
protocol: "https",
authToken: process.env.TWILIO_AUTH_TOKEN
}));
// Webhook middleware without validation (for development)
app.use("/dev-webhooks", webhook({ validate: false }));
// Route handler after middleware
app.post("/webhooks/voice", (req, res) => {
// Request is already validated by middleware
const response = new VoiceResponse();
response.say("Hello from validated webhook!");
res.type("text/xml").send(response.toString());
});Configuration options for fine-tuning webhook validation behavior.
interface RequestValidatorOptions {
/** Full webhook URL with query string (overrides host/protocol) */
url?: string;
/** Hostname used by Twilio in webhook configuration */
host?: string;
/** Protocol used by Twilio in webhook configuration */
protocol?: string;
}
interface Request {
/** Request protocol (http/https) */
protocol: string;
/** Get header value by name */
header(name: string): string | undefined;
/** All request headers */
headers: IncomingHttpHeaders;
/** Original request URL with query string */
originalUrl: string;
/** Raw request body (for body hash validation) */
rawBody?: any;
/** Parsed request body parameters */
body: any;
}Low-level utilities for generating and comparing webhook signatures.
/**
* Generate the expected signature for a given request
* @param authToken - Your Twilio auth token
* @param url - The full webhook URL with query parameters
* @param params - The request parameters
* @returns Expected signature string (base64 encoded)
*/
function getExpectedTwilioSignature(
authToken: string,
url: string,
params: Record<string, any>
): string;
/**
* Generate the expected SHA256 hash for a request body
* @param body - The plain-text request body
* @returns Expected body hash (hex encoded)
*/
function getExpectedBodyHash(body: string): string;
/**
* Validate request body against provided hash
* @param body - The request body string
* @param bodyHash - The hash to validate against
* @returns true if body hash matches
*/
function validateBody(
body: string,
bodyHash: string | Buffer | any[]
): boolean;Usage Examples:
// Manual signature verification
const expectedSignature = getExpectedTwilioSignature(
authToken,
"https://myapp.com/webhook?foo=bar",
{ From: "+1234567890", Body: "Hello" }
);
const providedSignature = req.headers["x-twilio-signature"];
const isValid = expectedSignature === providedSignature;
// Body hash verification
const bodyHash = getExpectedBodyHash(req.rawBody);
const isBodyValid = validateBody(req.rawBody, req.query.bodySHA256);Handle complex webhook validation scenarios including proxy servers, load balancers, and custom URL configurations.
Usage Examples:
// Validation behind reverse proxy
app.post("/webhook", (req, res) => {
const isValid = validateIncomingRequest(req, authToken, {
protocol: "https", // Force HTTPS even if proxy forwards HTTP
host: "myapp.com" // Use external hostname
});
});
// Validation with custom webhook URL
app.post("/webhook", (req, res) => {
const isValid = validateIncomingRequest(req, authToken, {
url: "https://myapp.com/webhook?custom=param"
});
});
// Multiple validation attempts for different URL formats
function validateWebhook(req, authToken) {
const signature = req.headers["x-twilio-signature"];
// Try different URL variations that Twilio might use
const urls = [
`https://${req.headers.host}${req.originalUrl}`,
`https://${req.headers.host}:443${req.originalUrl}`,
`http://${req.headers.host}${req.originalUrl}`,
`http://${req.headers.host}:80${req.originalUrl}`
];
return urls.some(url =>
validateRequest(authToken, signature, url, req.body)
);
}Important security considerations when implementing webhook validation.
Usage Examples:
// Always validate in production
const isProduction = process.env.NODE_ENV === "production";
app.use("/webhooks", webhook({
validate: isProduction, // Only skip validation in development
authToken: process.env.TWILIO_AUTH_TOKEN
}));
// Secure error handling
app.post("/webhook", (req, res) => {
try {
const isValid = validateRequest(/* ... */);
if (!isValid) {
// Log failed validation attempts
console.warn("Invalid webhook signature", {
ip: req.ip,
userAgent: req.headers["user-agent"],
timestamp: new Date().toISOString()
});
return res.status(403).send("Forbidden");
}
// Process valid webhook...
} catch (error) {
console.error("Webhook validation error:", error);
res.status(500).send("Internal Server Error");
}
});
// Rate limiting validation failures
const rateLimit = require("express-rate-limit");
const validationFailureLimit = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 failed validations per windowMs
skip: (req) => {
// Only count failed validations
const isValid = validateIncomingRequest(req, authToken);
return isValid;
},
message: "Too many invalid webhook attempts"
});
app.use("/webhooks", validationFailureLimit);interface IncomingHttpHeaders {
[key: string]: string | string[] | undefined;
}
type ValidationResult = boolean;
interface SignatureComponents {
/** The webhook URL used for signature generation */
url: string;
/** The request parameters included in signature */
params: Record<string, any>;
/** The computed signature hash */
signature: string;
}Install with Tessl CLI
npx tessl i tessl/npm-twilio