---
term: 'Webhooks (Event-Driven Webhooks)'
seoTitle: 'Webhooks Explained: HMAC Security, Retries, Idempotency'
headline: 'What are Webhooks?'
slug: webhooks
category: backend-compute
shortDefinition: 'A webhook is an automated HTTP callback: when an event occurs, one system POSTs a payload to a URL another system registered.'
relatedTerms:
  - event-driven-architecture
  - websockets-real-time-sync
  - cloud-code-serverless-functions
  - pub-sub-pattern
contrastsWith:
  - websockets-real-time-sync
aboutTerms:
  - 'Webhook Endpoint'
  - 'HMAC Signature Verification'
  - 'At-Least-Once Delivery'
faq:
  - question: 'What is a webhook in simple terms?'
    answer: 'An automated HTTP message one system sends to another the moment something happens — a doorbell instead of repeatedly checking the door. You give a provider a URL; when the event fires, it POSTs the event data there. The term dates to 2007: "user-defined HTTP callbacks."'
  - question: 'What is the difference between a webhook and an API?'
    answer: 'Direction and initiative. An API is request-driven — the client asks, the server answers. A webhook is event-driven — the server pushes when something happens, unasked. A webhook is really a pattern built on APIs, and most real integrations use both: webhooks to hear about changes, API calls to act on them.'
  - question: 'What is the difference between webhooks and polling?'
    answer: 'Polling asks on a timer and mostly hears "nothing yet" — measurement across a large automation platform famously found roughly 98% of polls return no new data. Webhooks invert it: zero requests at rest, immediate delivery on change. Polling survives as the reconciliation net under webhooks, not their rival.'
  - question: 'What is an example of a webhook?'
    answer: 'A payment platform POSTing to your backend when a charge succeeds; a git push triggering a CI build; a form submission appearing in a chat channel; a shipping provider announcing a delivery. Any "when X happens over there, tell my system" integration is webhook-shaped.'
  - question: 'How do I receive a webhook?'
    answer: 'Expose an HTTPS endpoint that accepts POST, register its URL with the provider, and select the events you want. In the handler: verify the signature, return a 2xx within seconds, and do real processing asynchronously. Test with a capture tool before wiring production logic.'
  - question: 'Are webhooks secure?'
    answer: 'Not by default — the endpoint is a public URL anyone can POST to. The standard defense is an HMAC signature: the provider signs each payload with a shared secret, and you recompute over the raw body and compare in constant time, rejecting stale timestamps to block replays. HTTPS always; IP allowlists as garnish.'
  - question: 'What happens if my endpoint is down?'
    answer: 'Good providers retry with exponential backoff, often for hours or days — which is why delivery is at-least-once and duplicates are normal. Events can still be lost past the retry window, so critical integrations reconcile with periodic API polls rather than trusting webhooks alone.'
  - question: 'How do I handle duplicate webhook deliveries?'
    answer: 'Dedupe on the event''s unique ID: record processed IDs and skip repeats, keeping the record at least as long as the provider''s retry window. At-least-once delivery plus an idempotent handler equals effectively exactly-once processing — the receiving side''s half of the reliability contract.'
  - question: 'What is the difference between a webhook and a WebSocket?'
    answer: 'A webhook is a stateless, one-way, server-to-server HTTP notification; a WebSocket is a persistent, two-way connection, built for client-facing real-time like chat and live dashboards. Server tells server: webhook. Server streams to user interfaces: WebSocket.'
  - question: 'Are webhooks delivered in order?'
    answer: 'No — retries and parallel delivery reorder events freely. Handlers should apply events by ID and timestamp or, better, treat the webhook as a doorbell: fetch the object''s current state from the API rather than reconstructing it from arrival order.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'webhooks.fyi — webhook best practices'
    url: 'https://webhooks.fyi/'
  - name: 'W3C WebSub Recommendation'
    url: 'https://www.w3.org/TR/websub/'
  - name: 'REST Hooks — resthooks.org'
    url: 'https://resthooks.org/'
  - name: 'OWASP SSRF Prevention Cheat Sheet'
    url: 'https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html'
cta:
  title: 'Webhooks in both directions'
  text: 'On Back4app, an afterSave trigger is an outgoing webhook and a Cloud Function is a ready-made receiver — verify the signature, write to your database, and let Live Queries push the result to every screen.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-30'
translationKey: webhooks
---

**A webhook is an automated HTTP callback: when an event occurs, one system POSTs a payload to a URL another system registered.** The term — coined in 2007 as "user-defined HTTP callbacks" — names the inversion that matters: instead of your system asking repeatedly whether anything changed, the other system tells yours the moment it does. It is push built from the web's plainest parts: an HTTPS URL, a POST, a JSON body, and a 2xx acknowledgment.

## Key takeaways

| Question | Answer |
| --- | --- |
| The mechanism | Register a URL → event fires → provider POSTs payload → you return 2xx |
| vs. an API | APIs answer when asked; webhooks speak when something happens |
| The security bar | HMAC over the raw body, constant-time compare, timestamp window |
| The delivery truth | At-least-once, unordered, retried — dedupe and reconcile |
| The receiver's mantra | Verify · ack fast · process async · dedupe by event ID |

## The delivery, end to end

```text
SETUP     receiver exposes  https://api.example.com/hooks/payments
          and registers it with the provider, choosing events + a secret

EVENT     charge succeeds at the provider

DELIVERY  POST /hooks/payments
          webhook-id: evt_8fk2            ← dedupe key
          webhook-timestamp: 1767024900   ← replay guard (inside the signature)
          webhook-signature: v1,d2Vio…    ← HMAC-SHA256(secret, id.timestamp.body)
          { "type": "charge.succeeded", "orderId": "o-1187" }

ACK       receiver verifies signature → 200 within seconds → work happens async
RETRY     no 2xx? exponential backoff for hours/days → duplicates are NORMAL
```

Both directions of the pattern in code — a data trigger as the sender, a Cloud Function as the receiver, and the client just watching the result:

**JavaScript:**

```javascript
// JavaScript — Cloud Code (cloud/main.js): both directions of a webhook
// OUTGOING: any data change can notify an external system
Parse.Cloud.afterSave('Order', async (req) => {
  if (req.object.get('status') !== 'paid') return;
  await Parse.Cloud.httpRequest({
    method: 'POST',
    url: 'https://hooks.example.com/orders', // the receiver's registered URL
    headers: { 'Content-Type': 'application/json' },
    body: { event: 'order.paid', id: req.object.id },
  });
});

// INCOMING: a Cloud Function is a ready-made webhook receiver
Parse.Cloud.define('paymentWebhook', async (req) => {
  verifySignature(req.params, process.env.WEBHOOK_SECRET); // HMAC first
  await markOrderPaid(req.params.orderId); // write fast, work async
  return { received: true }; // 2xx before heavy processing
});
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// The client's side of a webhook: watch its effect in real time
// (payment platform → Cloud Function receiver → database → Live Query → UI)
final orderQuery = QueryBuilder<ParseObject>(ParseObject('Order'))
  ..whereEqualTo('objectId', orderId);
final sub = await LiveQuery().client.subscribe(orderQuery);
sub.on(LiveQueryEvent.update, (order) {
  if (order.get<String>('status') == 'paid') showReceipt();
});
// The webhook itself was handled server-side — clients just watch the data.
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// The client's side of a webhook: watch its effect in real time
// (payment platform → Cloud Function receiver → database → Live Query → UI)
let orderQuery = Order.query("objectId" == orderId)
let sub = try await orderQuery.subscribe()
sub.handleEvent { _, event in
    if case .updated(let order) = event, order.status == "paid" {
        showReceipt()
    }
}
// The webhook itself was handled server-side — clients just watch the data.
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// The client's side of a webhook: watch its effect in real time
// (payment platform → Cloud Function receiver → database → Live Query → UI)
val orderQuery = ParseQuery.getQuery<ParseObject>("Order")
orderQuery.whereEqualTo("objectId", orderId)
val sub = ParseLiveQueryClient.Factory.getClient().subscribe(orderQuery)
sub.handleEvent(SubscriptionHandling.Event.UPDATE) { _, order ->
    if (order.getString("status") == "paid") showReceipt()
}
// The webhook itself was handled server-side — clients just watch the data.
```

## Webhooks vs. APIs vs. polling

| | Webhook | API call | Polling |
| --- | --- | --- | --- |
| Initiative | Provider pushes | Consumer asks | Consumer asks on a timer |
| Timing | At the event | On demand | At the next interval |
| Wasted traffic | None at rest | None | ~98% of polls find nothing |
| Direction | One-way notify | Two-way request/response | Two-way, repeated |
| Best at | "Tell me when" | "Do this / give me that" | Reconciliation, no-webhook providers |

The debate is largely false: mature integrations use all three — webhooks to hear about changes fast, [API](/glossary/api/) calls to fetch authoritative state and act, and a slow reconciliation poll as the net under the trapeze.

## The receiver's checklist

The list every provider's docs scatter and no explainer assembles:

1. **Verify the HMAC on the raw body** — before parsing; re-serialized JSON breaks signatures.
2. **Compare in constant time** — string equality leaks timing; use your crypto library's comparator.
3. **Enforce the timestamp window** — reject deliveries older than ~5 minutes; because the timestamp is *inside* the signed content, an attacker can't replay a validly signed old request with a fresh clock.
4. **Return 2xx fast** — within seconds, before heavy work; slow handlers get timed out and retried into duplicate storms.
5. **Process asynchronously** — enqueue, ack, then work.
6. **Dedupe by event ID** — with a memory at least as long as the provider's retry window.
7. **Don't trust the payload for critical actions** — treat the webhook as a doorbell; fetch current state from the provider's API before shipping goods or granting access.
8. **Log deliveries and alert on failures** — silence is indistinguishable from a broken endpoint.

One local-development note the definitional pages skip: `localhost` is unreachable from the internet, so development runs through a tunneling tool that lends your machine a public URL, plus a capture tool for replaying real payloads.

## Delivery semantics, honestly

Webhook delivery is **at-least-once**: the provider retries until acknowledged, so duplicates are a *feature* of reliability, not a bug in it — exactly-once delivery over an unreliable network is formally impossible, and the practical equivalent is at-least-once plus your idempotent handler. **Ordering is not guaranteed**: retries and parallel sends interleave, so `updated` can arrive before `created`; apply events by ID and version, or refetch state. And **retry windows end**: an endpoint down for a weekend can permanently miss events, which is why critical money-shaped integrations pair webhooks with periodic reconciliation — the [same at-least-once discipline](/glossary/pub-sub-pattern/) brokers formalize, arriving over plain HTTP. That comparison generalizes: a webhook is point-to-point push to a known URL; [pub/sub](/glossary/pub-sub-pattern/) adds a broker, topics, and fan-out; [WebSockets and SSE](/glossary/sse-vs-websockets-vs-polling/) serve *clients*, not servers. Webhooks are the answer specifically when two systems that don't share infrastructure need to hear about each other's events.

```mermaid
flowchart LR
  accTitle: Webhook delivery with retries and asynchronous processing
  accDescr: An event in the provider is queued and POSTed with an HMAC signature to the receiver's registered URL. The receiver verifies the signature, acknowledges quickly with a 2xx, and processes asynchronously with deduplication. Failed deliveries re-enter the provider's retry queue with exponential backoff, and repeated failures go to a dead-letter log.
  E["Event occurs"] --> Q["Provider queue<br/>+ HMAC signing"]
  Q -->|"POST payload"| R["Receiver endpoint"]
  R -->|"verify → 2xx fast"| A["Async worker<br/>dedupe by event ID"]
  R -.->|"no 2xx"| RT["Retry with backoff<br/>hours → days"] --> Q
  RT -.->|"exhausted"| DL["Dead-letter log<br/>+ alert"]
  A --> DB[("Your database")]
```

## Building the sender's side

Emitting webhooks reliably is its own small system, and no ranking page sketches it: a **queue per destination** so one dead endpoint doesn't block the rest; **retries with exponential backoff and jitter**; a **dead-letter store** with redelivery tooling after attempts run out; **HMAC signing** with per-endpoint secrets and rotation; **event-type subscriptions** so receivers opt into what they want; and **delivery logs** your customers can read, because "did you send it?" is the first support question. One security item unique to senders: receivers register arbitrary URLs, so validate them against internal address ranges — an attacker registering `http://10.0.0.5/admin` as their "webhook endpoint" is [server-side request forgery](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html) wearing an integration feature.

## Common use cases

- **Payment lifecycles** — charges, refunds, subscription changes announced to your backend as they settle.
- **CI/CD triggers** — the canonical git-push-starts-a-build wiring.
- **Cross-app automation** — form tools, chat platforms, and CRMs chained through each other's events.
- **Operational notifications** — monitoring alerts and delivery updates landing in team channels.
- **Data synchronization** — keeping a local mirror of a partner system current without polling its whole API.

## Should you use a webhook? A decision matrix

| Situation | Reach for |
| --- | --- |
| Another company's system must notify yours | Webhooks — the interop default |
| Your services, your infrastructure | [Pub/sub](/glossary/pub-sub-pattern/) — brokered, buffered, fan-out |
| Browsers/apps need live updates | [WebSockets / live queries](/glossary/websockets-real-time-sync/) |
| Provider offers no webhooks | Polling, politely |
| Money or access rides on the event | Webhook + verify-then-refetch + reconciliation |
| You're the platform emitting events | Build the sender's side above — or don't promise reliability |

## Limitations and trade-offs

- **Delivery is best-effort beyond the retry window.** Webhooks notify; they don't guarantee. Reconciliation polls backstop anything that must not be missed.
- **The receiver inherits uptime duty.** Your endpoint's availability now gates someone else's events — deploys, cold starts, and timeouts all become integration bugs.
- **Security is opt-in.** An unverified webhook endpoint is an unauthenticated write API; the HMAC checklist is the difference between integration and injection.
- **Debugging spans two companies.** Delivery logs on both sides and replay tooling are what turn "it didn't arrive" from a standoff into a diff.
- **Payloads drift.** Providers version event schemas; consumers pinning to exact shapes break quietly — parse defensively and ignore unknown fields.

## Webhooks on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Both halves of the pattern are one [Cloud Code](/glossary/cloud-code-serverless-functions/) construct away, as the code tabs show. **Outgoing:** an `afterSave` trigger watching your data calls `Parse.Cloud.httpRequest` to any registered URL — your app becomes a webhook provider by writing the function, and the sender's-side disciplines (retry on failure, log deliveries) live in the same file. **Incoming:** a Cloud Function exposed over HTTPS is a ready-made receiver — verify the HMAC against a secret in server-side config, write the result to the database, return fast — and the update then fans out to every open screen via [Live Queries](/glossary/real-time-live-queries/), completing the loop from a payment platform's event to a user's receipt without a server to run at either step.
