What Is a Webhook? How Apps Notify Each Other
A webhook is a message one service sends to your app when something happens, like a payment. How webhooks work, why signatures and retries matter, and pitfalls.
Mythex Team · · 6 min read
A webhook is an automatic message that one service sends to your app the moment something happens. When a customer's payment succeeds, Stripe sends a webhook to an address in your app saying "payment succeeded, here are the details", and your app reacts — marks the order paid, sends a receipt, unlocks the account. In short: an API is your app asking another service a question; a webhook is the other service calling your app to tell it something.
Why it matters when you build with AI
As soon as your app takes payments, receives form submissions from another tool, or connects to services like GitHub, Shopify or a calendar, you'll run into webhooks. They're usually the correct way to know something happened — but they're also where AI-built apps are most often subtly wrong:
- The endpoint accepts any request, so anyone can fake a "payment succeeded" event.
- The app grants access on the checkout redirect page instead of on the webhook, so people who close the tab early never get what they paid for, and people who find the success URL get it free.
- The same event arrives twice and the customer gets two orders or two emails.
Knowing how webhooks work lets you ask for the protections up front.
An everyday analogy: a delivery text
You can find out if your parcel has arrived in two ways. You can keep walking to the front door every ten minutes to check (that's polling — your app repeatedly calling an API to ask "anything new?"). Or the courier can text you when it's delivered (that's a webhook).
The text is clearly better — as long as you can trust it. If anyone could send you a "parcel delivered" message, you'd want some way to know it really came from the courier. And if your phone was off when the text came, you'd want the courier to try again later. Those two concerns — verification and retries — are most of what makes webhooks tricky.
How a webhook works, step by step
- You create an endpoint in your app's backend: a URL such as
https://yourapp.com/webhooks/stripethat acceptsPOSTrequests. - You register that URL with the provider and choose which events you want (for example, "checkout completed" and "subscription cancelled").
- The provider gives you a signing secret. You store it as a secret on your server — see environment variables and secrets.
- An event happens. The provider sends an HTTP request to your URL with a JSON body describing it, plus a signature header.
- Your app verifies the signature, records the event ID, replies with a success code (
200) quickly, and then does the work. - If your app doesn't reply with success, many providers try again later.
Signatures
A signature proves the request came from the provider and wasn't altered. Stripe, for example, computes an HMAC (a kind of keyed fingerprint) with SHA-256 over the request body and a timestamp, using the signing secret, and sends it in a Stripe-Signature header. Your app does the same calculation with its copy of the secret; if the results match, the request is genuine. Because the timestamp is signed too, you can reject old requests someone recorded and replays later — Stripe's libraries default to a five-minute tolerance, according to its documentation as of September 2026.
One practical detail: the signature is computed on the raw request body. If your framework parses and re-formats the JSON before you verify, the check fails. This is a very common bug.
Retries, duplicates and order
Providers differ, so check each one's documentation. As of September 2026:
| Behaviour | Stripe | GitHub |
|---|---|---|
| Automatic retries | Yes, for up to three days with increasing gaps in live mode | No; you redeliver failed deliveries manually or by script |
| Response time | Reply with a 2xx quickly, before any slow work | A delivery that takes longer than 10 seconds to answer counts as failed |
Stripe's documentation also says events aren't guaranteed to arrive in the order they happened, and that the same event can occasionally arrive more than once, so it recommends tracking event IDs.
The general lesson holds for any provider: events can arrive late, more than once and out of order. Your handler should be idempotent — processing the same event twice has the same effect as processing it once.
A worked example: unlocking a paid course
You sell an online course. A student clicks Buy, pays on Stripe's hosted checkout page, and is sent back to your site.
The fragile way: the "thank you" page unlocks the course. If the student closes the browser during the redirect, they paid but have no access. If someone guesses the thank-you URL, they get access without paying.
The webhook way:
- When Stripe confirms the payment, it sends a
checkout.session.completedevent to/webhooks/stripe. - Your backend verifies the signature with the signing secret. If it fails, it replies
400and does nothing. - It checks whether that event ID has already been processed. If yes, it replies
200and stops. - It saves the event ID, replies
200, and then grants the student access and sends a welcome email. - The thank-you page just says "Payment received — your course will appear in a moment" and checks the student's access status.
Later, if a subscription is cancelled or a card fails, other webhook events tell your app to remove or pause access. This is how subscription products stay in sync with billing — see what SaaS is and how to add payments to your app.
Key terms
- Endpoint: the URL in your app that receives webhooks.
- Event / payload: what happened, and the data describing it (usually JSON).
- Signing secret: the shared secret used to verify signatures.
- Idempotent: safe to run more than once with the same result.
- Polling: repeatedly asking an API for changes instead of waiting for a webhook.
- Tunnel: a tool that gives a laptop a temporary public URL so webhooks can reach it during development.
Common mistakes and misconceptions
- Not verifying signatures. The endpoint URL is not a secret. Verification is what makes the request trustworthy.
- Doing slow work before replying. Sending emails or generating PDFs before responding can cause timeouts, which trigger retries, which cause duplicates.
- Assuming exactly once, in order. Record event IDs and don't rely on arrival order.
- Using a preview URL. Webhooks need a stable, public address. Private previews and
localhostaren't reachable from the internet; use your published URL (or a tunnel while testing). - Mixing test and live secrets. Providers often issue different signing secrets for test and live modes. Store both and use the right one.
- Trusting the payload blindly for important decisions. For high-stakes actions, it's reasonable to fetch the latest state from the provider's API after receiving the event.
What to ask your AI builder for
Add a
POST /webhooks/stripeendpoint on the server. Verify the Stripe signature using the raw request body and the secretSTRIPE_WEBHOOK_SECRET. Reject invalid signatures with 400. Store each event ID and skip events already processed. Reply 200 quickly, then handlecheckout.session.completedby marking the order paid and sending the receipt. Log the event type. Don't grant access from the checkout success page.
Then test it: send a test event from the provider's dashboard, send the same one again, and send a request with a wrong signature. Only the first should change anything.
Webhooks on Mythex
Mythex doesn't run a separate webhooks service; you ask the agent to add a webhook endpoint in your app's own backend, and point the provider at your published URL, since webhooks need a public address. The signing secret goes into your project secrets, and if you change it on a live app, you publish again. The docs recipe on handling webhooks has a starter prompt, and store templates are a good starting point for apps that take payments.
Questions
What is a webhook in simple terms?
A webhook is an automatic message that one service sends to a web address in your app when something happens, such as a payment succeeding or a form being submitted. Instead of your app asking repeatedly whether anything changed, the other service tells it.
What is the difference between a webhook and an API?
With an API call, your app asks another service for something and gets an answer. With a webhook, the other service calls your app when an event happens. Many integrations use both: webhooks to hear about events, API calls to fetch details or take action.
Are webhooks secure?
They can be, if you verify them. Well-designed providers sign each webhook with a secret shared only with you, and your app should reject any request whose signature doesn't match. Without that check, anyone who finds the URL could send fake events.
What happens if my app is down when a webhook is sent?
It depends on the provider. Stripe, for example, retries failed deliveries for up to three days in live mode, while GitHub does not retry automatically and expects you to redeliver. Check each provider's documentation, and design your handler to cope with late or repeated events.