{"id":"webhook-twilio-verify","version":"1.0.0","description":"Verify a Twilio X-Twilio-Signature against a URL and form params or a JSON body, trying Twilio's port-variant URLs.","supported_operations":["verify twilio signature","check x-twilio-signature","validate an incoming twilio webhook","is this really from twilio","twilio request validator equivalent","check an sms webhook came from twilio"],"unsupported_operations":["computing a fresh X-Twilio-Signature for a test delivery (see webhook-signature-compute)","verifying any other vendor webhook scheme (see webhook-stripe-verify, webhook-slack-verify, webhook-standard-verify)"],"semantics":["params and body are mutually exclusive; providing both, or providing body without a 'bodySHA256' query parameter present in url, is invalid_input.","The signing string (no params, or params given): url followed by, for each key in params sorted ascending by Unicode code point (as Python's sorted() in twilio-python, not JS UTF-16 code-unit order), that key's DEDUPLICATED values sorted the same way, each immediately appended as key+value with no separators (a repeated form field becomes one key+value pair per distinct value, in sorted order).","The tool additionally computes the same signing string over two more URL forms and treats a match against ANY of the three as valid: with_default_port (port 443 for https / 80 for http, added only when url has no explicit port) and without_port (any explicit port stripped). matched_url_variant reports which of the three (tried in order as_given, with_default_port, without_port) produced the match; null when none match.","When body is given and url's query string contains bodySHA256=<hex>: the tool first computes SHA-256(body) hex and compares it to that query value; a mismatch is reason 'body_hash_mismatch' regardless of the signature. Only once the body hash matches (or no body was given) does the tool compute HMAC-SHA1(auth_token, signing_string) (base64) and compare it to signature across the three URL variants; a failure there is 'signature_mismatch'.","auth_token is used verbatim as UTF-8 bytes; it is never echoed in the output or in any error detail.","Limits on params (checked before any sorting or hashing, limit_exceeded): at most 1000 values in total (max_params_values; a string value counts 1, an array counts its length, duplicates included, before deduplication), and at most 65536 UTF-8 bytes summed over every key (once per key) and every value (each array element, duplicates included) (max_params_bytes)."],"limits":{"max_url_bytes":4096,"max_body_bytes":262144,"max_params_bytes":65536,"max_params_values":1000},"pricing":{"status":"unpriced","charge_usd":null},"input_schema":{"type":"object","additionalProperties":false,"required":["url","signature","auth_token"],"properties":{"url":{"type":"string","minLength":1,"maxLength":4096},"params":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":1}]}},"body":{"type":"string","maxLength":262144},"signature":{"type":"string","minLength":1,"maxLength":512},"auth_token":{"type":"string","minLength":1,"maxLength":512}},"oneOf":[{"additionalProperties":false,"required":["url","signature","auth_token"],"properties":{"url":{"type":"string","minLength":1,"maxLength":4096},"params":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":1}]}},"signature":{"type":"string","minLength":1,"maxLength":512},"auth_token":{"type":"string","minLength":1,"maxLength":512}}},{"additionalProperties":false,"required":["url","body","signature","auth_token"],"properties":{"url":{"type":"string","minLength":1,"maxLength":4096},"body":{"type":"string","maxLength":262144},"signature":{"type":"string","minLength":1,"maxLength":512},"auth_token":{"type":"string","minLength":1,"maxLength":512}}}]},"output_schema":{"type":"object","additionalProperties":false,"required":["valid","reason","matched_url_variant"],"properties":{"valid":{"type":"boolean"},"reason":{"type":["string","null"],"enum":["signature_mismatch","body_hash_mismatch",null]},"matched_url_variant":{"type":["string","null"],"enum":["as_given","with_default_port","without_port",null]}}},"examples":[{"input":{"url":"https://mycompany.com/myapp.php?foo=1&bar=2","signature":"zYQTYrRWXE7LtzbG4PfP7/bkkGo=","auth_token":"12345"},"output":{"valid":true,"reason":null,"matched_url_variant":"as_given"}},{"input":{"url":"https://mycompany.com/myapp.php?foo=1&bar=2","signature":"wrongsignaturewrongsignature=","auth_token":"12345"},"output":{"valid":false,"reason":"signature_mismatch","matched_url_variant":null}}],"execute_url":"/v1/tools/webhook-twilio-verify/versions/1.0.0/execute"}