Story
What Is a Webhook and How Does It Let Systems Talk Without Polling?
I am a webhook. In one sentence: instead of you asking me "did anything happen yet?" over and over, I call you the instant something actually does. I'm a callback -- a URL you register in advance, and a promise that when the event you care about fires, I'll deliver a message to that URL with the details, unprompted, the moment it's true.
What I actually do, in order
Most integrations start with the lazy version of watching for change: repeatedly asking a system "anything new?" on a timer. That works, but it wastes calls when nothing happened and it's always a little late, because you only find out on your next scheduled check.
I flip that relationship. My job runs in three steps:
1. You register interest. You give the source system a URL and tell it which event you want to hear about -- a pull request opened, a deploy finished, a payment succeeded, a ticket moved to Ready. 2. The event happens. Something changes on the source system's side, on its own schedule, not yours. 3. I fire. The source system sends an HTTP request to your URL, carrying the event payload, the moment the event occurs. Your system receives it, parses it, and reacts. No asking required.
I am push, not pull. The source system does the work of noticing; you just have to be listening.
How I actually work here
In this operation, I'm the reason a CI run finishing doesn't require anyone to sit refreshing a status page -- the pipeline fires me the moment it completes, and whatever's listening reacts immediately, whether that's posting a result, unblocking a merge, or waking up an agent that was waiting on exactly that signal. I'm underneath a lot of "the moment X happens, Y happens automatically" behavior in this codebase: a PR event triggering a review pass, a deploy event triggering a health check, a ticket-state change triggering a downstream notification.
I also don't care what's on the other end. I don't know if my payload lands in a queue, a database write, or a running agent process -- I just deliver the request and consider my job done. What happens after delivery is entirely the receiver's problem, which is exactly why I compose so cleanly with almost anything.
Where I fail
This is the part that matters more than the part above, so I'm not going to bury it.
I assume you're listening, and I don't wait around if you're not. If your endpoint is down, slow, or returns an error when I call, most implementations of me will retry a few times on a backoff schedule and then give up. If you were down for the whole retry window, that event is gone unless the source system also gives you a way to replay it. I am fundamentally at-least-once, sometimes-zero-times if nobody built in redelivery.
I can deliver the same event more than once. Network retries, source-side bugs, and "did that actually send" ambiguity all mean a receiver has to treat every delivery as possibly a duplicate. If your handler isn't idempotent -- if receiving the same "payment succeeded" event twice causes you to record it twice -- I will eventually cause that bug, not because I misbehaved, but because at-least-once delivery is the honest guarantee I can actually make.
I have no built-in ordering guarantee. If two events fire close together, I don't promise you'll receive them in the order they happened. A "ticket closed" event can arrive before the "ticket reopened" event that logically preceded it, especially under retry conditions. If your logic assumes strict ordering, I will eventually violate that assumption.
I am a blind trust relationship unless you verify me. Anyone who discovers your endpoint URL can send it a request shaped like a real event. Without a signature check on the payload, your system has no way to tell a genuine event from a forged one. I don't authenticate myself by default -- that has to be built into how I'm consumed.
I go silent when the network between us is unreliable, and nothing tells you that happened. Unlike a poll, where a failed check is visible the moment you look, a dropped webhook delivery can vanish without either side noticing, unless someone is watching delivery logs or has a reconciliation job that double-checks state independently.
Tech-Tips
- Make every handler idempotent. Key on the event's unique ID, not just its contents, and skip processing if you've already seen that ID. Assume duplicates will happen; don't hope they won't. - Verify the signature on every payload. If the sender supports signing the request body, check it before trusting a single field. An unsigned webhook endpoint is an open door. - Never assume delivery order. Design handlers to be correct regardless of which event arrives first, or include a sequence/version field in the payload and let the receiver reconcile order itself. - Add a reconciliation fallback for anything that matters. For high-stakes events, pair me with an occasional poll or status check that catches the case where I silently failed to deliver. Belt and suspenders, not either/or. - Respond fast, process slow. Acknowledge receipt quickly (a 200) and do the actual work asynchronously. A slow handler that makes the sender wait is the single most common reason webhook delivery starts failing and retrying in the first place.
Evidence: This piece describes the push-notification callback pattern as it is used throughout our internal deploy, CI, and ticket-lifecycle event notifications -- no vendor or specific product is named, per the moat-protection and STORY-FORMAT conventions. Evidence class: internal operating record and Owner attestation, 2026-08-25.