Engineering

The webhook endpoint anyone can call

Airtable's help page for the incoming webhook trigger answers the security question in one line. Does Airtable handle signature verification? "Airtable does not support signature verification in the webhooks automation trigger." The same page warns that anyone who has the URL for your webhook trigger can trigger your automation. None of that is hidden or misleading. It does mean that if the automation behind that URL writes records, the URL is the only thing standing between your base and whoever else has seen it.

Verification needs the exact bytes that arrived

Stripe signs every event it sends. The signature goes in the Stripe-Signature header, computed from a timestamp and the raw request body against your endpoint's whsec_ secret. The docs say why in one sentence: without verification, an attacker could send fake webhook events to your endpoint to trigger actions like fulfilling orders, granting account access, or modifying records. GitHub does the same job with X-Hub-Signature-256, an HMAC hex digest of the payload prefixed with sha256=.

Stripe attaches a warning to that which matters more than it looks. Verification runs on the raw body, and any manipulation of the raw body makes it fail. Re-serialising the JSON breaks it. So does a framework that hands your handler a parsed object and discards the original bytes.

That is the awkward shape of the problem on a platform whose job is to parse things for you. Zapier ships two webhook triggers. Catch Hook parses the request body into fields you can map, which is why most people pick it. Catch Raw Hook returns the body unparsed, capped at 2MB, and it is the one that also includes headers. Build on Catch Hook and the signature header never reaches your Zap, while the bytes it signed are already gone.

The platforms are not all in the same place on this

Airtable is the clearest case, because it tells you. The incoming webhook trigger takes POST only, caps payloads at 100kb, allows five requests per second, and does not verify signatures. Your control is that the URL is hard to guess. That URL also lives in the webhook settings screen of whatever third party service you pasted it into, plus any support ticket or screenshot it has travelled through since.

Xano earns some credit here, because it can do the thing. Xano publishes a guide to verifying Stripe webhooks on its own platform: pull the timestamp and signature out of the Stripe-Signature header, concatenate them with the payload, hash the result with HMAC SHA-256 using the signing secret from your environment variables, then put a precondition in front of the endpoint that returns Access denied when the computed signature does not match. The primitive exists. What you build with it is a hand-wired chain of steps in a function stack, plus a custom function you have to remember to drop in front of every new endpoint that writes.

Make's webhook documentation covers the queue, the limit of 667 queue items per 10,000 monthly credits, the rate limit, and the three default responses a webhook returns when you have no response module: 200 Accepted, 400 Queue is full, 429 Too many requests. Checking who sent the request before the scenario runs is not on that page.

The version you can put a test around

Supabase's own example of this is short. You set verify_jwt = false and auth: 'none' on the function, which switches off the platform's credential check, then verify Stripe's signature inside the handler with constructEventAsync and a SubtleCryptoProvider, because Deno has no synchronous Node crypto. A bad signature returns 400 and the handler stops. The whole file runs about fifteen lines, and the Supabase docs are careful to say that auth: 'none' makes your handler fully responsible for authenticating the caller.

The fifteen lines are not the part worth paying for. GitHub publishes a test vector for this exact computation: the secret "It's a Secret to Everybody" and the payload "Hello, World!" have to produce 757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17. You can assert that in CI, and it goes red the day someone adds a body parser to the middleware chain and breaks the raw bytes without noticing. GitHub also tells you to compare the digests with crypto.timingSafeEqual rather than ==, which is the kind of detail a visual step editor gives you nowhere to put.

When we moved Stay World Class off Webflow and Xano onto Next.js, NestJS and Supabase, the headline numbers were Lighthouse 55.91 to 91 out of 100 and LCP 77% faster. The gain on the inbound side was less photogenic. Request handling became code in a repository, so a signature check had somewhere to live and a test to prove it still worked after the next refactor.

When staying put is the right call

If the only caller is your own script and the URL has never left your infrastructure, a secret URL over HTTPS is a reasonable control. That is roughly how a bearer token works anyway. Airtable's 100kb payload ceiling and five-per-second limit are unlikely to bother you in that setup either.

Blast radius decides most of the rest. An automation that posts to Slack when a form arrives can be forged into posting a junk Slack message, and you will survive that. An automation that grants account access or marks an invoice paid is a different conversation, and those are two of the three examples Stripe names in its own warning.

If you are already on Xano, you have what you need. Build the verification function, put the precondition in front of the endpoints that write, and do not move a backend over this one issue. The case for leaving low-code is cumulative. A single unverified webhook is a weekend of work. It is the tenth thing shaped like it that tells you the platform has stopped saving you time.

Sources

Is your platform outgrowing its stack?

Book a call and we'll walk through how we'd approach your platform, with an estimate grounded in a working prototype.