Guides
How Do I Receive Webhooks From Third-Party Platforms?
How to receive webhooks from third-party platforms: expose an endpoint, verify signatures, ack fast, dedupe, and process asynchronously, plus where it gets hard across many providers and how Paragon handles it.

Garrett Scott
,
Head of Marketing
How Do I Receive Webhooks From Third-Party Platforms?
Last updated: July 2026
To receive a webhook from a third-party platform, register a public HTTPS endpoint; the platform sends an HTTP POST there each time a subscribed event fires. Your job is four things: verify the payload, store it durably before returning a 2xx, deduplicate so retries don't double-process, and do the real work on a queue instead of inline.
The hard part isn't a single webhook. It shows up when you do this across every app your customers use, each with its own auth, payload shape, and retry behavior. This guide covers how to receive one webhook well, why it gets harder with every provider you add, and how a unified approach like Paragon's ActionKit Triggers collapses that per-provider work into one API.
What is a webhook, and how is it different from polling?
A webhook is a message a third-party platform sends you when something happens, so you don't have to keep asking. Instead of your service calling Stripe every minute to check for new records (polling), Stripe calls you the moment a record changes.
Polling burns API quota and adds latency: you find out about a change on your next poll, not when it happens. Webhooks push the event to you in near real time. The cost is that a webhook puts an endpoint on the public internet that has to stay up, verify what it receives, and tolerate duplicates. And not every provider offers webhooks, so polling never fully goes away; you keep it as the fallback for the apps that don't push.
How do I receive a webhook, step by step?
Expose an HTTPS endpoint, subscribe to the events you want, then verify, acknowledge, and process each delivery. Here is the sequence most providers expect:
Expose a public HTTPS endpoint. Something like
POST /webhooks/stripe, reachable from the public internet over TLS.Subscribe to the events. Register your endpoint with the provider, through its dashboard or its subscription API. This step is where providers diverge the most (more on that below).
Verify before you trust anything. Check the signature header against your shared secret, and if the provider signs a timestamp, check it for freshness (see the next section). Do this first, on every request.
Store it durably, then return
2xx. Providers expect an acknowledgment in single-digit seconds; budget for the tightest one (about 1 to 3 seconds) or they treat the delivery as failed and retry. The catch: a2xxtells the provider "I have this," so only send it once the event is written somewhere durable. Acking before you persist is how events quietly disappear when your process restarts.Deduplicate with an atomic write. Delivery is at-least-once, so you will see the same event more than once. Make the store write an atomic insert keyed on the provider's event ID; a duplicate hits the unique constraint and becomes a no-op. A read-then-write check is a race under concurrent retries, so let the database enforce it.
Process asynchronously, and don't assume order. A worker reads from the store and does the real work off the request path. Assume events can arrive out of order (a "record updated" can beat its "record created"), so reconcile against the provider's current state or an event version rather than trusting arrival order.
Here is the shape of it in Node and Express:
How do I verify a webhook is authentic?
Verify the signature the provider sends in the request headers against a shared signing secret, and reject anything that doesn't match. Your endpoint is public, so anyone who learns the URL can POST to it; the signature is how you tell a real event from a forged one.
Most providers sign the raw request body with HMAC-SHA256 and put the result in a header (Stripe-Signature, X-Hub-Signature-256, and so on). You recompute the HMAC with your secret and compare. Three things trip people up. Verify against the raw bytes rather than the re-serialized JSON, because parsing and re-encoding changes the bytes and breaks the hash. Use a constant-time comparison so you aren't leaking the secret through timing. And remember that a valid signature proves the payload is authentic, not that it is fresh: an attacker who captures one signed request can replay it later. Many providers sign a timestamp alongside the body for exactly this reason, so reject anything outside a short tolerance (Stripe uses five minutes). Two related details worth planning for: some providers make you answer a one-time verification challenge before they start sending events, and you will want a path to rotate the signing secret without downtime.
Why does receiving webhooks get hard across many platforms?
One webhook is a weekend project. Receiving webhooks reliably from every app your customers connect is a system you have to own and keep running. The work piles up in five places:
Every provider is different. Stripe signs one way, GitHub another, and Salesforce has no simple Stripe-style webhook (you go through Change Data Capture and the Streaming API). Different subscription APIs, payload schemas, retry policies, and timeouts, and those schemas drift as providers version their events.
Some don't support webhooks. For those you build and run polling, with its own scheduling, cursors, and rate-limit handling.
Auth is per customer. In multi-tenant software you subscribe on behalf of each user, which means storing and refreshing OAuth tokens for every customer and re-subscribing when a token rotates. This is usually the layer that gets ugly fastest. (We wrote about the managed authentication side of this separately.)
Events have to route to the right tenant. An incoming payload has to map back to the specific customer it belongs to. Get it wrong and you leak one customer's data into another's account.
No silent failures. You need retries with backoff, dead-letter handling for what never delivers, and logs that tell you what fired, what delivered, and what didn't, broken out per provider and per customer.




