Synchronous Calls, Asynchronous Messaging, and Webhooks
Choose between synchronous APIs, queued messages, and webhooks by matching delivery behavior to latency needs, failure handling, and ownership boundaries.
Synchronous HTTP fits bounded requests that need an immediate answer; queues hand work off for later, and webhooks deliver events to other systems. The article explains what acknowledgements promise, how retries create duplicates, and where durable acceptance, an outbox, and idempotency fit. Use these patterns to set clear delivery contracts, monitor failures, and protect webhook receivers.
Synchronous Calls, Asynchronous Messaging, and Webhooks
Introduction
Backend integrations can wait for an immediate response, hand work to a queue, or notify another system with a webhook. Each pattern changes how latency, retries, delivery guarantees, and failure recovery work across the boundary.
This article compares synchronous calls, asynchronous messaging, and webhooks, including what an acknowledgement means and how to handle duplicate delivery. It also covers durable acceptance, observability, and security for producers and consumers.
When to use each approach
| Pattern | Use it when | Trade-off |
|---|---|---|
| Synchronous HTTP | The operation is short and the caller needs an immediate answer | Latency and availability are coupled across services |
| Asynchronous messaging | Work can finish later, traffic is bursty, or consumers need independent recovery | More state, monitoring, and duplicate-safe handlers |
| Webhook | A partner needs event notifications without polling | Delivery is at least once in practice; receiver endpoint must be reachable |
Do not use a queue merely to hide a slow API if the user experience still requires an immediate result. Do not expose a synchronous endpoint that waits on long-running work with arbitrary timeouts. For long operations, return 202 Accepted with a status URL and document what “accepted” guarantees.
Implementation sketch: acknowledge, then process
A minimal HTTP handler should validate, persist, and enqueue before acknowledging. The durable write and queue publication should be atomic or coordinated with an outbox; otherwise a crash between them can lose work. See the outbox pattern for that failure boundary.
async function submitExport(request: Request): Promise<Response> {
const input = await request.json();
const job = await jobs.createAndEnqueue(input); // durable operation
return Response.json(
{ id: job.id, statusUrl: `/exports/${job.id}` },
{ status: 202 },
);
}
For a webhook receiver, verify the signature over the raw request body before trusting event fields, persist a delivery identifier, and return success only after durable acceptance. Keep business processing off the request path if it may exceed the sender’s timeout.
Production failures and mitigations
A synchronous dependency slows down and consumes every caller’s connection pool. Set deadlines, cap concurrency, and fail with a clear retryable response. A queue consumer can fall behind after a traffic spike; alert on oldest-message age and scale consumers against safe downstream capacity. A webhook endpoint can return 500 after it has already committed the event, causing a duplicate delivery; use an idempotency key and acknowledge only after storing the delivery.
| Failure scenario | What the caller sees | Mitigation |
|---|---|---|
| The database commit succeeds but queue publication fails | The request appears accepted, but no worker sees the job | Use a transactional outbox and alert on unpublished outbox records |
| A worker finishes a side effect, then crashes before acknowledging the message | The broker redelivers work that already ran | Make handlers idempotent and record completion by stable message ID |
| A poison message fails on every delivery | Retries consume capacity and block useful work | Cap retries, move the message to a dead-letter queue, and provide a replay process |
| A webhook receiver commits an event but its response times out | The sender retries a delivery the receiver already stored | Deduplicate by event ID and return success after durable acceptance |
Observability checklist
- Record request or event IDs across producer, queue, worker, and receiver logs.
- Measure synchronous latency and error rates by dependency and operation.
- Track queue depth, oldest-message age, retry count, and dead-letter volume.
- Track webhook delivery attempts, response codes, and acknowledgement lag.
- Avoid logging full payloads when they contain personal or secret data.
Security and Compliance Notes
Use TLS, authenticate callers, authorize each operation, and validate webhook signatures with constant-time comparison. Rotate signing secrets and include a timestamp to limit replay windows, while still handling legitimate retries. Define event schemas and versioning so a new optional field does not break older consumers. A common mistake is treating HTTP 200 as proof that downstream business work completed; define exactly whether it means received, persisted, or finished.
For compliance, send only the fields a consumer needs, and set retention and deletion rules for queued messages, dead-letter queues, and webhook delivery records. Restrict access to payloads and keep secrets or personal data out of operational logs; retain event IDs and delivery metadata when an audit trail is required.
Quick Recap Checklist
- Use synchronous requests when the caller needs an immediate result and the work is bounded.
- Return
202 Acceptedonly after work is durably queued, with a way to check its outcome. - Treat webhook and queue delivery as repeatable, and deduplicate with stable event identifiers.
- Document retry behavior, acknowledgement meaning, and failure handling for both sides.
Interview Questions
Further Reading
- Outbox Pattern — coordinate database writes and message publication.
- Retries, Timeouts, Backoff, and Circuit Breakers — handle transient failures in synchronous calls.
- Idempotency, Deduplication, and Safe Replays — make repeat deliveries safe.
- API Gateways and Service Boundaries — understand HTTP boundaries between clients and services.
- RFC 9110: HTTP Semantics — request-response status and behavior, including
202 Accepted. - AsyncAPI Specification — describe channels, messages, and asynchronous operations.
- CloudEvents Specification — a common envelope for event metadata across services.
Conclusion
Choose the communication pattern from the caller’s actual need. Keep short request-response work synchronous, move long or bursty work behind durable messaging, and use webhooks when another system needs event notifications. State what each acknowledgement means, because that contract determines what both sides can safely do next.
Category
Related Posts
Partial Failure, Ordering, and Eventual Consistency
Understand partial API failures, message ordering, and eventual consistency, then design status models and recovery paths clients can reason about.
Event Envelopes and Metadata for Reliable EDA
Learn how event envelopes separate transport context from payload, apply CloudEvents attributes, propagate correlation IDs, and validate messages safely.
Event Security and Sensitive Data in EDA
Secure event-driven systems with least-privilege identities, encrypted transport and payloads, careful data minimization, and a deliberate retention plan.