Most modern business systems are interconnected. A payment system needs to tell accounting that the money has arrived, e-commerce needs to notify the warehouse that an item is to be shipped, and the CRM wants to know when a user registers. The question ishowthey talk to each other. The simple, but worse, answer is that one system periodically asks another: "has anything new happened?" The smarter answer is that the system that knows something speaks directly. It is the core of webhooks and event-driven architecture.
Polling vs webhooks: the difference in practice
Pollingmeans that your system regularly calls another and asks for news. Every thirty seconds, every minute, every hour. It's easy to build, but wasteful: the vast majority of calls return "nothing new". You pay for traffic and capacity without getting information, and you always get a delay between something happening and you discovering it.
Awebhookreverses the logic. Instead of you asking, the dispatcher calls you when something actually happens. Technically, a webhook is just an HTTP POST: the provider's system sends a small packet of data to a URL you've registered with them. Stripe, GitHub, Shopify, Fortnox and thousands of other services work that way. You enter an address thathttps://dittforetag.se/api/webhooks/stripe, and the second a payment succeeds, that address receives a notification.
The advantage is real time and efficiency. The disadvantage is that you are now running a publicly accessible endpoint that receives data from the outside world - and that makes demands that many underestimate.
Event-driven architecture: a step beyond individual webhooks
Webhooks are often the first step towards a broader idea:event-driven architecture(EDA). The idea is that systems do not call each other directly and wait for a response, but publishevents- "order created", "payment completed", "customer terminated" - which other systems can listen to and react to independently.
It givesloose coupling: whoever creates an order does not need to know that there is an inventory, invoicing and email system that cares. They subscribe to the event individually. If you add a new system later, it just connects to the same stream, without touching the original code. For a growing company, it's the difference between an architecture that becomes increasingly tangled and one that can be expanded piece by piece.
Webhooks outward, queues inward
In practice, robust solutions combine two things.Webhooksused to communicate with external parties - your payment provider sends webhooks to you, and you send webhooks to, for example, a third-party logistician.Message queues and event buses(such as AWS SQS, RabbitMQ, Azure Event Grid or Apache Kafka) are used internally, between your own services, where you want control, guaranteed delivery and the ability to replay events.
A typical flow: a payment generates an internal event, the event is put on a queue, background processes pick it up and post and send a receipt, and only then a webhook goes out to an external system. Webhooks where it meets the outside world, queues where you need reliability.
Queues: why you almost always want one
A common rookie mistake is to let the webhook endpoint do all the work at once: receive the call, update the database, send the email, call three other APIs - and only then respond to the sender. It is fragile. If any of the steps is slow or fails, the sender has time to give up.
Stripe, for example, waits up to ten seconds for a response in the 2xx range. If you do not respond in time, the delivery will be considered a failure, even if your code is finally ready. The solution is simple and powerful:receive, verify, queue, reply 200 - and do the heavy lifting asynchronously.The endpoint is fast and the answer always arrives on time. If a later step fails, the event remains in the queue and can be rerun without the sender having to do anything.
Queues also give you load balancing. If you get a thousand events in a second, you don't have to process a thousand things at the same time - they lie in a queue and are grazed at the pace your workers can handle. For larger volumes and multiple concurrent consumers, a log-based platform like Kafka becomes relevant, as it both scales to very high flows and allows you to replay the history.
Idempotence: the rule that saves you from duplicates
The most important concept in this entire field - and the most overlooked - isidempotency. An operation is idempotent if it produces the same result regardless of whether it is executed once or several times. "set the balance to 100" is idempotent. "Adding 100 to the balance" is not.
Why does it matter? Because webhook providers can explicitly send the same event more than once. Stripe is clear about that: in case of network mess or retries, you can get the same event multiple times, and your handler has to handle it. If each received "payment completed" creates a new invoice, the customer suddenly receives three invoices for one purchase.
The solution is based on each event having onestable, unique IDwhich does not change between retries (with Stripe it starts withpossibly_). The pattern is:
Read the event ID when it comes in.
Check in a database or cache if you already processed that ID.
If you have it - answer 200 and do nothing more.
If you don't have it - process the event and save the ID as processed.
Save processed IDs with a reasonable lifetime, often on the order of a week to a month, so that retries that appear much later are still caught. A related matter:never rely on events to come in the right order.Stripe does not guarantee delivery order, and neither do most others. Build your logic so that an "updated" event that comes before a "created" one doesn't trigger it.
Retries: plan for things sometimes going wrong
The internet is unreliable. Your system will be down for maintenance, a deploy will go awry, a database will hang. Good webhook providers handle it withretry with exponential backoff- they try again, but with increasingly longer intervals.
Stripe tries to deliver for up to three days in its live mode, with a schedule that starts immediately and then thins out to every twelve hours. This means that even if your endpoint is down for a while, the events will arrive as soon as you are back. But it only works if you follow the rules: respond with a 2xx code when you've received and queued the event, and respond with an error code if you actually couldn't receive it - so the provider knows to try again. If you answer 200 to something you dropped, the event is lost forever.
Also think about it the other way around: sendingyouwebhooks to your customers or partners, you need to build retries yourself, backoff and a log of what succeeded - otherwise you become the unreliable party.
Security: your endpoint is open to the entire internet
A webhook URL can basically be called by anyone who knows it. You must therefore be able to prove that a call really comes from who you think it is. The default for that isHMAC signatures: the sender calculates a signature of the message with a secret key known only to the two of you, and sends it in a header. You calculate the same signature on your page and compare.
Some hard rules:
Sign over the raw bodyexactly as it was received - not over the JSON you parsed and re-serialized, because the slightest format difference destroys the signature.
Use a time-safe comparisonof signatures, not regular string similarity, so as not to leak information via response times.
Check a timestampand reject calls that are too old, usually older than a few minutes, to protect against replaying a hijacked request later.
A modern trend is that the long-lived secret keys are beginning to be replaced by short-lived, automatically rotated signing keys. Regardless of the mechanism, the principle applies: never blindly trust incoming data, and treat a webhook endpoint with the same seriousness as any other public API surface.
Standardization: CloudEvents
A recurring problem is that each provider has its own format for its events.CloudEvents, a project under the Cloud Native Computing Foundation that became a mature "graduated" standard in January 2024, tries to remedy that by defining a common envelope around each event - fields thatid, source, typeandhours- regardless of who sent it. It makes your events compatible with platforms like Knative, Azure Event Grid and most integration tools. If you are building new, it is worth considering from the start.
When are you actually going to use this?
Event-driven architecture is powerful but not free - it's harder to debug and reason around than a straight call that waits for a response. Rule of thumb:
Use webhookswhen you integrate with external services and want to react in real time to things like payments, orders or registrations.
Introduce queues and event-driven architecture internallywhen you have several services that must react to the same events, when volumes grow, or when you need loose coupling to be able to expand the system without tearing up old code.
Keep it simpleif you have two systems talking synchronously and everything works. Don't add an event bus just because it sounds modern.
Used correctly, this is the backbone of systems that feel fast, connect seamlessly and withstand stress. Used incorrectly, it becomes a source of lost data and hard-to-find bugs - and the difference is almost always in the details around queues, idempotence, retries and signatures.
At ZORC, we build integrations and event-driven solutions where systems talk to each other reliably in real time - with idempotency, retries and secure signature management built in from the start. If you want to know what an integration would mean for you, describe the project this springquote calculatoror get in touch viacontactthen we'll look at it together.