The Event-Driven APIs & Webhooks pattern is used when servers need to notify consumers of changes asynchronously, rather than requiring clients to poll for updates. This pattern exposes:
- A subscription endpoint (e.g.,
POST /webhook-subscriptions) - A callback delivery contract (e.g., POSTs sent to consumer endpoints)
- Optional retry, signing, and idempotency behavior
Common examples:
- Order status changed
- Payment succeeded/failed
- Inventory updated
- User profile updated
- Long-running job completed
This pattern complements — rather than replaces — synchronous APIs.
11.1. Overview
Event-driven APIs allow publishers (your system) to send notifications or full payloads to subscribers (clients) whenever relevant changes occur.
Characteristics:
- Push-based: server initiates the callback
- Asynchronous delivery
- Optional event payload describing what changed
- Retry strategy for delivery failures
- Signature verification / HMAC for security
- Often used as part of an integration platform or webhook framework
This pattern significantly reduces polling load and improves responsiveness.
11.2. When to Use This Pattern
Use Webhooks / Event-Driven APIs when:
- Clients need to know about mutations as soon as they happen.
- Polling would be expensive, inefficient, or slow.
- Third-party systems need to react to changes (e.g., CRM, ERP, payment systems).
- You want to support loose coupling between systems.
- Human-facing products need real-time updates without using streaming (e.g., mobile apps receiving notifications via your backend).
Examples:
POST /webhook-subscriptions-
Outbound events such as:
order.shippedpayment.failedcustomer.updated
11.3. When NOT to Use This Pattern
Avoid webhooks when:
- The client cannot expose a public HTTP endpoint.
- Delivery needs strict ordering guarantees (use message queues instead).
- Consumer systems cannot handle duplicate deliveries.
- You need high-frequency updates (streaming APIs may be a better fit).
- Security requirements disallow sending data to third-party URLs.
Also avoid using webhooks:
- As a replacement for synchronous APIs
- For data that requires strong consistency (webhooks are eventually consistent)
11.4. What the Pattern Looks Like
A canonical event-driven workflow:
- Client subscribes to events:
POST /webhook-subscriptions
{
"eventType": "order.shipped",
"callbackUrl": "https://client.example.com/webhooks/order-shipped"
}
-
Server validates and stores the subscription.
-
When an event occurs, server delivers it:
POST https://client.example.com/webhooks/order-shipped
{
"eventId": "evt_567",
"eventType": "order.shipped",
"occurredAt": "2024-05-04T12:00:00Z",
"data": {
"orderId": "or_12345",
"trackingNumber": "1Z999"
}
}
- Client responds with:
200 OK
If delivery fails, server retries according to its retry strategy.
11.5. Anti-Patterns to Avoid
❌ No verification/signature on webhook calls → Anyone could impersonate your service.
❌ No idempotency → If retries happen, the consumer may duplicate work.
❌ Assuming guaranteed delivery → Webhooks are best-effort; always include retry + dead-letter strategies.
❌ Sending overly large event payloads → Webhooks are best for lightweight messages; large payloads should be fetched via follow-up API calls.
❌ Failing to store subscription metadata → E.g., invalidating credentials or callback URLs without lifecycle events.
11.6. OpenAPI Examples
This includes:
POST /webhook-subscriptionsGET /webhook-subscriptions/{subscriptionId}- Webhook event schema (as delivered to client)
openapi: 3.0.3
info:
title: Webhooks API - Event-Driven APIs & Webhooks Pattern Example
version: 1.0.0
servers:
- url: https://api.example.com
paths:
/webhook-subscriptions:
post:
summary: Create a webhook subscription
tags: [Webhooks]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateWebhookSubscriptionRequest'
examples:
default:
value:
eventType: "order.shipped"
callbackUrl: "https://client.example.com/webhooks/order-shipped"
secret: "whsec_abc123"
responses:
'201':
description: Webhook subscription created
headers:
Location:
schema:
type: string
example: https://api.example.com/webhook-subscriptions/sub_12345
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSubscription'
examples:
default:
value:
subscriptionId: "sub_12345"
eventType: "order.shipped"
callbackUrl: "https://client.example.com/webhooks/order-shipped"
secret: "whsec_abc123"
/webhook-subscriptions/{subscriptionId}:
get:
summary: Get webhook subscription details
tags: [Webhooks]
parameters:
- in: path
name: subscriptionId
schema:
type: string
example: sub_12345
required: true
responses:
'200':
description: Subscription details
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSubscription'
'404':
description: Not Found
components:
schemas:
CreateWebhookSubscriptionRequest:
type: object
properties:
eventType:
type: string
example: "order.shipped"
callbackUrl:
type: string
example: "https://client.example.com/webhooks/order-shipped"
secret:
type: string
example: "whsec_abc123"
required:
- eventType
- callbackUrl
WebhookSubscription:
type: object
properties:
subscriptionId:
type: string
example: "sub_12345"
eventType:
type: string
example: "order.shipped"
callbackUrl:
type: string
example: "https://client.example.com/webhooks/order-shipped"
secret:
type: string
example: "whsec_abc123"
required:
- subscriptionId
- eventType
- callbackUrl
WebhookEvent:
type: object
description: Payload that your API delivers to a subscriber's webhook endpoint.
properties:
eventId:
type: string
example: "evt_567"
eventType:
type: string
example: "order.shipped"
occurredAt:
type: string
format: date-time
example: "2024-05-04T12:00:00Z"
data:
type: object
example:
orderId: "or_12345"
trackingNumber: "1Z999"
required:
- eventId
- eventType
- occurredAt
- data
11.7. Visualizing the Event-Driven & Webhooks Pattern (Mermaid)
sequenceDiagram
autonumber
participant C as Client System
participant API as Webhooks API
participant EVT as Event Producer (Orders)
participant WH as Client Webhook Endpoint
Note over C,API: Client subscribes to events
C->>API: POST /webhook-subscriptions<br/>{ eventType, callbackUrl, secret }
API-->>C: 201 Created<br/>{ subscriptionId }
Note over EVT,API: An event occurs in domain system
EVT->>API: publish(event: order.shipped)
Note over API,WH: API delivers webhook event
API->>WH: POST callbackUrl<br/>{ eventId, eventType, data }
WH-->>API: 200 OK
alt Delivery fails
API->>WH: Retry with exponential backoff
end