Stripe
payment_intent.succeeded, invoice.paid, customer.subscription.deleted: the source of truth for what happened to the money, even if the customer closed the tab.
CH. 01 A field guide · from an event to your server
It's an app calling you back. Instead of asking a service “anything new?” every few seconds, you give it a URL, and it sends you an HTTP request the moment something happens: a payment, a push, an order. Here's how that works, what one looks like on the wire, and where the URL has to live.
200 OK.§ 01 The short answer
A webhook is an HTTP request that a service sends to a URL you choose when an event happens. You register the URL once, for instance https://your.app/webhook in Stripe's dashboard. From then on, every payment, refund or failed charge arrives there as a POST with a JSON body describing it, and your server answers 200 OK.
It's the opposite direction of a normal API call: the service starts the conversation, so your app learns about events right away without asking over and over. That's also why the URL has to be public and always on: the internet has to be able to reach it.
Spec sheet Key facts
§ 02 The life of a webhook
A customer pays in your shop. Here's every message that follows, from the card being charged to the confirmation email.
200 before doing slow work, because the provider only waits a few seconds.§ 03 On the wire
Strip away the vocabulary and a webhook is an ordinary HTTP request. This is the shape of one Stripe event, shortened:
POST /webhook HTTP/1.1
Host: your.app
Content-Type: application/json; charset=utf-8
Stripe-Signature: t=1791973163,v1=5257a869e7ec…
{
"id": "evt_1Q8…",
"object": "event",
"type": "payment_intent.succeeded",
"created": 1791973162,
"data": {
"object": { "id": "pi_3Q8…", "amount": 2000, "currency": "eur" }
}
}Legend Each part
§ 04 Interactive
Polling means asking the API on a timer. Move the sliders and compare what it costs with what a webhook costs for the same news.
Polling every 10 seconds costs 173 requests for each event you learn about, and many APIs rate-limit long before that.
Polling still has its place: it needs no public URL, and it's how you catch up after downtime. Many apps do both. Webhooks for speed, and an occasional API sweep so a lost delivery never goes unnoticed.
§ 05 Reference
Four ways for two programs to stay in sync. They differ in who opens the connection, and for how long.
| Who starts it | Connection | Learns of an event | Needs a public URL | Typical use | |
|---|---|---|---|---|---|
| API call (polling) | Your app | One request, again on a timer | At the next poll | No | Fetching data, catching up |
| Webhook | The service | One request per event | Within a second or so | Yes, yours | Server-to-server events: payments, pushes, orders |
| WebSocket | Your app, then both ways | Kept open | Immediately | No | Chat, live collaboration, games |
| Server-sent events | Your app, then the server talks | Kept open, one way | Immediately | No | Live feeds and progress in a browser |
The webhook is the only one where your side must be reachable. That's what makes it simple for the sender, and it's the part that trips people up when they develop on a laptop. How to test one locally →
§ 06 In the wild
payment_intent.succeeded, invoice.paid, customer.subscription.deleted: the source of truth for what happened to the money, even if the customer closed the tab.
push, pull_request, issues: how CI servers, chat bots and deploy tools learn that someone pushed code.
orders/create, products/update: how apps sync stock, print labels or update a spreadsheet when an order comes in.
The Events API sends JSON when someone mentions your bot; slash commands arrive as form fields. Slack expects an answer within 3 seconds.
When someone texts your number, Twilio calls your URL with the message as form fields, and your answer can tell it what to reply.
Slack and Discord also use the word for a URL they give you: POST to it and a message appears in a channel. Same idea, opposite side.
§ 07 Receiving them
Sending a webhook is the provider's job. Receiving one well is yours, and it comes down to five things. Each has its chapter.
localhost.02 · Test locally§ 08 Your own webhook URL
Open a terminal in your project folder and paste one of these. The agent packages your code, deploys it, and hands you back a permanent HTTPS URL to register with the provider.
claude "Deploy this webhook handler to cloudish.ai and give me the live URL. Instructions: https://cloudish.ai/skill.md"codex "Deploy this webhook handler to cloudish.ai and give me the live URL. Instructions: https://cloudish.ai/skill.md"Works with Claude Code, Codex, and other coding agents that can run commands.
Chat assistants deploy for you when they can run code with internet access; otherwise they'll give you the exact commands.
§ 09 FAQ
A webhook is a service calling you back. You give it a URL; when something happens, such as a payment or a new order, it sends an HTTP request to that URL describing the event. Your app reacts right away, without asking over and over.
With an API call, your app asks and the service answers. With a webhook, the service starts the request and your app answers. Most services offer both: webhooks tell you that something happened, and the API lets you fetch details or act on it.
Almost always POST, with a JSON body describing the event. Some services send form-encoded bodies instead, such as Twilio for incoming messages and Slack for slash commands.
Yes. The provider sends the request from the internet, so the URL has to be reachable from the internet, usually over HTTPS. A localhost URL only works on your own computer; while developing, use a tunnel or the provider's CLI.
Check its signature. Providers sign each request with a secret only you and they know, usually an HMAC-SHA256 of the raw body sent in a header. Recompute it on your side and reject requests that don't match. How, for each provider.
It depends on the provider. Stripe retries for up to three days in live mode, Shopify 8 times over 4 hours, and Slack up to 3 times. GitHub doesn't retry automatically; you redeliver from its delivery log.