Twilio's SMS and voice webhooks are application/x-www-form-urlencoded POSTs —
a wall of From=%2B15550100&Body=Hello… that's painful to read raw. Capture one
and see it decoded field-by-field before you write your handler.
$ curl https://catchhook.catchhook.workers.dev/new
Console → Phone Numbers → your number → Messaging (or Voice) → set
"A message comes in" to your https://catchhook.catchhook.workers.dev/h/… URL. Text your number.
The capture appears in your dashboard with the form body decoded into a table:
From, To, Body, MessageSid,
SmsStatus, NumMedia, plus geo fields like FromCity —
and headers including X-Twilio-Signature.
Note on X-Twilio-Signature: it's HMAC-SHA1 over the URL + sorted
params, not the raw body, so CatchHook's body-HMAC badge doesn't apply — validate it in your
app with Twilio's helper library (RequestValidator). You can still eyeball the
header here and confirm it changes when params change.
Twilio executes whatever TwiML your endpoint returns. In the bin's settings, set the
response Content-Type to text/xml and the body to:
<?xml version="1.0" encoding="UTF-8"?>
<Response><Message>Got it — you said: {{body.Body}}</Message></Response>
Text your number again: Twilio replies with your message, echoing the inbound text via the
{{body.Body}} template variable (form fields work in templates too). You've built an
auto-responder without deploying anything — and every inbound message is still captured.
Run ./catchhook relay YOUR_BIN http://localhost:3000
(curl https://catchhook.catchhook.workers.dev/cli to get the script) and every Twilio POST is re-delivered
to your local server with the original form body, byte-identical. No ngrok, no tunnel — it's
outbound polling, so it works behind NAT and corporate proxies.
No signup needed. Or from your terminal: curl https://catchhook.catchhook.workers.dev/new