Quick Answer
Every serious API provider, Stripe and Resend and Auth0 included, signs its webhooks so you can prove they're genuine. Your AI agent almost never checks that signature. That leaves the door open: anyone can send a fake webhook and your app will happily believe it. api-doctor catches this before you commit.
The problem: faking a webhook is trivial
A webhook is just an HTTP POST. And anyone on the internet can send an HTTP POST to your endpoint. That's the whole vulnerability in one sentence.
Here's the kind of handler your agent tends to write:
app.post('/stripe-webhook', (req, res) => {
const event = req.body;
if (event.type === 'charge.succeeded') {
updatePaymentStatus(event.data.object.id, 'completed');
}
res.sendStatus(200);
});
This code assumes req.body came from Stripe. It didn't have to. An attacker can hand-craft a request, and your app cheerfully marks a payment as complete, no money required.
Providers solved this years ago: every webhook carries a signature header (Stripe-Signature, Auth0-Signature, Resend-Webhook-Signature) that's a cryptographic signature of the body. You verify it, and you only trust the ones that pass.
So why does the AI miss it? Because verification isn't part of the happy path, it's a security wrapper around it. The model has seen plenty of webhook handlers in its training data, just not always the guardrails, so it ships the handler and skips the guard.
A real example: a Resend webhook with no verification
Say your agent writes a handler that trusts the payload without checking the signature. An attacker sends a fake bounce event for a competitor's email address. Your app dutifully marks it bounced. Their outreach quietly breaks, and nobody can figure out why.
Resend spells it out in the docs: every webhook includes a Resend-Signature header, and you should verify it before processing anything. The AI never got to that page. It generated the happy path and moved on.
How api-doctor catches this
✗ Resend: 1 critical issue
resend-webhook-signature-verification-missing
src/webhooks.ts:8
Webhook handler does not verify Resend-Signature. Attackers can spoof webhook events.
Severity: critical
Security: CWE-347 (Improper Verification of Cryptographic Signature)
You fix it before it ever ships.
How to fix it: verify webhook signatures
For Resend:
import crypto from 'crypto';
app.post('/resend-webhook', (req, res) => {
const signature = req.headers['resend-signature'];
const secret = process.env.RESEND_WEBHOOK_SECRET;
const hash = crypto
.createHmac('sha256', secret)
.update(req.rawBody)
.digest('hex');
if (hash !== signature) {
return res.sendStatus(401);
}
const event = req.body;
if (event.type === 'email.bounced') {
markEmailAsBounced(event.data.email);
}
res.sendStatus(200);
});
For Stripe:
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
app.post('/stripe-webhook',
express.raw({ type: 'application/json' }),
(req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
return res.sendStatus(400);
}
if (event.type === 'charge.succeeded') {
// safe to process
}
res.sendStatus(200);
}
);
Frequently asked questions
Q: Do I still need to verify if my app is behind a firewall? Yes, every time. Assume any endpoint reachable from the internet will eventually be poked by someone who shouldn't be there.
Q: What if I'm using a webhook library? Stripe's official SDK handles verification for you. The moment you start parsing raw requests yourself, verification becomes your job again.
Q: How careful do I need to be with the webhook secret? Very. Keep it in environment variables and never let it near a git commit.