How to Test Twilio Webhooks
If you are building an application that handles incoming SMS messages or voice calls, Twilio relies on webhooks to notify your server. The moment someone texts your Twilio phone number, Twilio makes an HTTP POST request to your configured webhook endpoint. Your server must respond with TwiML (Twilio Markup Language) to tell Twilio what to do next.
Testing these webhooks is unique because Twilio formats its data differently than most modern APIs. Here is how to capture, inspect, and route Twilio webhooks effectively.
Understanding the Twilio payload format
Unlike Stripe or GitHub, which send webhooks with a application/json content type, Twilio sends webhooks as form data (application/x-www-form-urlencoded).
When a user sends an SMS, Twilio's POST request body looks like a URL query string:
ToCountry=US&ToState=CA&SmsMessageSid=SM123...&NumMedia=0&ToCity=LOS+ANGELES&FromZip=90210&SmsSid=SM123...&FromState=CA&SmsStatus=received&FromCity=BEVERLY+HILLS&Body=Hello+world&FromCountry=US&To=%2B1234567890&ToZip=90001&NumSegments=1&MessageSid=SM123...&AccountSid=AC123...&From=%2B1987654321&ApiVersion=2010-04-01
This format can be difficult to read at a glance, especially when trying to extract the Body (the actual message) or the From phone number during debugging.
Capturing Twilio webhooks
Stop reading raw JSON
Payloader shows you what your Twilio webhook actually did — in plain English. See the event, amount, status, and more at a glance.
Start free trial →Before writing the code to parse this form data, you should capture a real request. Do not try to guess the format from the documentation alone.
Using a dedicated tool like Payloader simplifies this dramatically:
- Create a new endpoint URL in Payloader.
- Log in to your Twilio console, navigate to your active phone number, and paste the Payloader URL into the "A MESSAGE COMES IN" webhook field.
- Send a text message from your personal phone to your Twilio number.
Payloader captures the request instantly. Because Payloader has built-in intelligence for Twilio, it automatically parses the x-www-form-urlencoded string and translates it into a human-readable summary. You will clearly see the sender's phone number, the recipient number, and the exact message body without having to manually decode the URL string.
Routing to your local machine
To write your application logic, you need those incoming SMS events delivered to your local server.
Instead of rotating ngrok URLs every time you restart your tunnel, you can configure Payloader to forward the incoming Twilio webhook directly to your localhost. This gives you a permanent URL to leave in your Twilio console while routing traffic to your development environment dynamically.
Furthermore, when your local code fails to respond with valid TwiML, you do not need to pull out your phone and send another text message. You can simply click "Replay" in Payloader to fire the exact same HTTP POST request at your local server until your code works.
Verifying Twilio signatures
Because Twilio webhooks instruct your application to perform actions (like sending a reply SMS, which costs money), security is critical. Anyone who knows your webhook URL can send a fake POST request pretending to be Twilio.
Twilio secures its requests using a cryptographic signature passed in the X-Twilio-Signature HTTP header.
To verify the request, your server must take the full webhook URL, sort the form POST parameters alphabetically, append them to the URL, and hash the resulting string using your Twilio Auth Token. If your calculated hash matches the X-Twilio-Signature header, the request is authentic.
This hashing logic is notoriously finicky. If your framework alters the form parameters or changes the URL scheme, the verification will fail. By using an inspector to see exactly what headers and raw bytes Twilio is sending, you can debug your signature verification logic much faster.