Acumatica · Events

Event-Driven APIs 2026 — A Field Guide

Event-Driven APIs 2026 — A Field Guide is the work that makes the systems talk. The API is the contract between the producer and the consumer; the contract is what determines.

John Kihiu12 min read

Request/response REST is a fine default, but it forces the client to poll for state it doesn't yet have, or forces the server to hold a connection open waiting for something to happen. Event-driven APIs flip that: the server pushes a notification when there's something worth knowing, and the client reacts. That shift — webhooks, streaming endpoints, and AsyncAPI as the contract format for describing it all — is now common enough that treating request/response as the only shape an API can take is itself a design smell for a certain class of problem. Here's what actually changes when you design an API around events instead of endpoints.

Webhooks are the baseline, not the exotic option

A webhook is the simplest possible event-driven API: you register a URL, the provider POSTs an event payload to it when something happens, and your server processes it asynchronously instead of blocking a request on it. Stripe, GitHub, and Twilio all built their integration story around webhooks because it removes polling entirely — no client hitting GET /orders?status=updated every 30 seconds hoping something changed. The API design work that's easy to skip and expensive to skip is signature verification and retry semantics: every webhook payload should be signed (HMAC over the raw body with a shared secret) so a receiver can reject forged requests, and the receiver's endpoint needs to be idempotent, because the sender will retry on timeout and you will receive the same event twice.

Webhooks need a receipt, not just a 200

Returning 200 before you've durably queued the event is a common webhook bug — if your process crashes between accepting the request and finishing the work, the event is gone and the sender thinks it was delivered. Write the event to a queue or table inside the same request before returning success.

AsyncAPI: OpenAPI for things that aren't request/response

OpenAPI describes endpoints, methods, and request/response schemas — it has no vocabulary for "this service publishes an event called order.shipped to this channel." AsyncAPI fills that gap: it's a specification format, structurally similar to OpenAPI, for describing channels, messages, and the producers/consumers on each side. The payoff is the same one OpenAPI gives REST — codegen for client and server stubs, contract testing, and a document a consuming team can read without asking you what fields are in the payload. It's most valuable once you have more than one team consuming your events, because at that point "here's a Slack message describing the JSON shape" stops scaling.

YAML · ASYNCAPI CHANNEL
asyncapi: 3.0.0
info:
  title: Orders Event API
  version: 1.2.0
channels:
  orderShipped:
    address: orders.shipped
    messages:
      OrderShipped:
        payload:
          type: object
          required: [order_id, shipped_at, carrier]
          properties:
            order_id: { type: string }
            shipped_at: { type: string, format: date-time }
            carrier: { type: string, enum: [ups, fedex, dhl] }
operations:
  onOrderShipped:
    action: receive
    channel:
      $ref: '#/channels/orderShipped'

Streaming, webhooks, and polling — picking the right shape

These three aren't interchangeable, even though they all solve "give me updates." Polling is appropriate when updates are infrequent and the client can tolerate delay — it's simple and needs no inbound connectivity from the provider. Webhooks fit when the provider can reach the consumer's network and events are relatively low-frequency and independent of each other. Streaming — Server-Sent Events for one-directional server-to-browser pushes, WebSockets for bidirectional, or a gRPC/Kafka stream for service-to-service — fits high-frequency or ordered event sequences where the client needs to react in near real time and can maintain a persistent connection. A common mistake is reaching for WebSockets for something that's really a handful of webhook events a day; the operational cost of a stateful connection isn't worth it below a certain event rate.

Designing consumers for at-least-once delivery

Nearly every event-driven API — webhooks especially — delivers at-least-once, not exactly-once. The provider will retry on a timeout, a 5xx, or a dropped connection, and it cannot tell the difference between "you didn't get it" and "you got it but your ack was lost." That means every consumer needs an idempotency key check: store processed event IDs (Stripe and GitHub both include one in every payload) and short-circuit if you've seen it before. Skipping this is the single most common bug in production webhook handlers — a duplicate "payment succeeded" event that isn't deduplicated will double-fulfill an order.

Versioning without breaking every subscriber at once

REST APIs version the URL or a header; event APIs version the payload, and it's harder to force every consumer to upgrade in lockstep because you often don't control when they redeploy. The safer path is additive-only changes for as long as possible — new optional fields, never removed or repurposed ones — and when a breaking change is unavoidable, publish both the old and new event shape for a deprecation window (a new event type like order.shipped.v2, or a schema_version field consumers branch on) rather than flipping the switch on a single date.

PatternGood fitDelivery
PollingInfrequent updates, simple clientsClient-pulled, no push infra
WebhooksLow-to-medium frequency, independent eventsAt-least-once, needs signing + idempotency
SSEServer-to-browser live updatesPersistent, one-directional
WebSockets / streamingHigh-frequency, bidirectional, orderedPersistent, stateful

None of this replaces REST — most APIs are still, correctly, request/response for anything that's a direct query or command. The event-driven pieces earn their place specifically where a client needs to know about something it didn't ask for, and the discipline that makes them reliable is unglamorous: sign your webhooks, version your payloads additively, document channels in AsyncAPI once more than one team depends on them, and assume every event will be delivered more than once.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.