If your backend pushes events into Kafka, sends MQTT messages to IoT devices, or streams updates over WebSockets, you have probably hit the same wall: OpenAPI does not describe any of that well. OpenAPI was built for request-response REST. It assumes a client calls a URL and waits for a JSON body to come back. Pub/sub does not work like that. AsyncAPI is the answer the API community settled on, and the 3.0 release in late 2023 cleaned up enough rough edges that 2026 is the year teams running event-driven systems finally start adopting it in production.
This guide explains what AsyncAPI is, how it compares to OpenAPI, what it actually describes (channels, messages, operations, servers), which protocols it supports, and who should bother adopting it. If you came here because you write REST API documentation and someone on your team mentioned AsyncAPI in a planning meeting, by the end of this post you will know whether to push for it or push back.
What is AsyncAPI?
AsyncAPI is an open-source specification for describing event-driven, asynchronous APIs. It is a YAML or JSON document that captures every channel your service publishes to or subscribes from, every message format that crosses those channels, every protocol binding (Kafka, AMQP, MQTT, WebSocket, NATS, SNS, SQS), and every server your producers and consumers connect to. The spec is machine-readable, so you can generate documentation, client code, server stubs, and validation logic from a single source.
The shorthand most people use: AsyncAPI is the OpenAPI of event-driven architectures. That framing is correct enough to be useful and wrong enough to be misleading. It is correct because the two specs share an almost identical document structure, were started by overlapping contributors, and serve the same purpose, machine-readable contracts. It is misleading because the underlying communication model is different. REST has endpoints and verbs. Async has channels and messages, and the same service can be both producer and consumer.
The current stable version is AsyncAPI 3.0, released November 2023, which split operations out of channels and made the spec significantly less ambiguous than 2.x. If you are starting a new file today, write asyncapi: 3.0.0.
Why AsyncAPI exists
OpenAPI does a great job of describing one thing: a synchronous HTTP request. The client sends a method and a path, the server returns a status and a body, and the contract is complete. Async does not fit that shape. A producer drops a message into a Kafka topic and never waits for a response. A consumer subscribes to a topic and receives messages whenever the broker has them. There is no path, no method, and no response body in the REST sense.
AsyncAPI was created in 2017 by Fran Méndez specifically because OpenAPI could not describe message-driven architectures without ugly workarounds. By 2026, event-driven adoption has reached the point where the spec has real momentum: 86% of IT leaders cite data streaming as a top strategic priority, according to Confluent's 2025 Data Streaming Report. Event-driven systems are not a niche anymore, and the teams running them need a way to share contracts that does not involve a Confluence page and a prayer.
What AsyncAPI describes
The 3.0 spec has four primary sections that map to how event-driven systems actually work. If you can fill these in, you have a complete contract.
Servers
Servers describe the brokers your producers and consumers connect to. Each server has a URL, a protocol (kafka, amqp, mqtt, ws, nats, sns, sqs, jms, http for SSE, etc.), an optional protocol version, and security requirements. A single AsyncAPI document can describe multiple servers, useful when the same logical channel exists in dev, staging, and production with different broker addresses.
Channels
Channels are the addressable destinations your application reads from or writes to. In Kafka, a channel is a topic. In AMQP, it is an exchange and routing key. In MQTT, it is a topic string with optional wildcards. The channel definition lists which messages can flow through it and any protocol-specific binding (for example, a Kafka channel can declare partition counts and key schemas).
Operations
Operations describe what your service does on a channel. There are exactly two actions in 3.0: send and receive. A send operation means your service publishes to the channel; a receive operation means your service consumes from it. This is the cleanest improvement in 3.0 over 2.x, where the same operation could be ambiguous about who does what.
Messages
Messages are the payloads that cross a channel. Each message has a name, a content type, headers, and a payload schema (usually JSON Schema, but Avro and Protobuf are supported via schema format extensions). Messages are reusable across channels, so you can define a UserSignedUp event once and reference it from any topic that carries it.
A minimal AsyncAPI 3.0 example
Here is the smallest useful AsyncAPI document, a service that consumes signup events from Kafka and publishes notification commands to a separate topic.
asyncapi: 3.0.0
info:
title: Notification Service
version: 1.0.0
description: Reacts to user signups by dispatching welcome notifications.
servers:
production:
host: kafka.prod.example.com:9092
protocol: kafka
description: Production Kafka cluster
channels:
userSignups:
address: user.signups.v1
messages:
UserSignedUp:
$ref: '#/components/messages/UserSignedUp'
notificationCommands:
address: notification.commands.v1
messages:
SendWelcomeEmail:
$ref: '#/components/messages/SendWelcomeEmail'
operations:
onUserSignup:
action: receive
channel:
$ref: '#/channels/userSignups'
dispatchWelcome:
action: send
channel:
$ref: '#/channels/notificationCommands'
components:
messages:
UserSignedUp:
contentType: application/json
payload:
type: object
required: [userId, email, signedUpAt]
properties:
userId: { type: string, format: uuid }
email: { type: string, format: email }
signedUpAt: { type: string, format: date-time }
SendWelcomeEmail:
contentType: application/json
payload:
type: object
required: [userId, template]
properties:
userId: { type: string, format: uuid }
template: { type: string, enum: [welcome_v1, welcome_v2] }
That is roughly 40 lines and it tells you the broker, the topics, the direction of data flow, and the exact shape of each event. A REST equivalent would describe two endpoints; the AsyncAPI version describes a service that participates in a flow.
Which protocols does AsyncAPI support?
The spec is intentionally protocol-agnostic at its core, with bindings packages that handle protocol-specific details. The mainstream protocols covered by official bindings:
- Kafka: topics, partitions, consumer groups, key schemas
- AMQP 0.9.1 (RabbitMQ): exchanges, queues, routing keys, durability
- MQTT 3 / 5: topics, QoS levels, retain flags, last-will messages
- WebSocket: query parameters, headers, subprotocols
- NATS: subjects, queue groups
- JMS: destinations, headers
- SNS / SQS: topics, queues, IAM roles
- HTTP (Server-Sent Events): paths, query, headers
- Anypoint MQ, Solace, Pulsar, IBM MQ, STOMP, Mercure: community bindings
If you use one of those, AsyncAPI can describe it without you inventing convention. If you have a homegrown transport, you can still use AsyncAPI; you just declare the protocol as custom and document the binding behavior in prose.
AsyncAPI vs OpenAPI
Both specs solve the same problem (machine-readable API contracts) but for different shapes of API. Here is the side-by-side that actually matters when you are deciding what to use.
| Dimension | OpenAPI | AsyncAPI |
|---|---|---|
| Communication style | Request / response (synchronous) | Pub/sub, streams, events (asynchronous) |
| Primary unit | Path + method (GET /users/{id}) | Channel + operation (receive from user.signups) |
| Transport | HTTP / HTTPS | Kafka, AMQP, MQTT, WebSocket, NATS, etc. |
| Direction | Client always initiates | Either side can be producer or consumer |
| Request body / response | Distinct, paired | One message schema per direction |
| Tooling maturity | Massive: Swagger, Postman, Stoplight, dozens more | Smaller but growing: AsyncAPI Studio, CLI, Generator |
| File extension | openapi.yaml | asyncapi.yaml |
| Spec versions in active use | 3.0, 3.1, 3.2 | 2.6, 3.0 |
If your API is REST, use OpenAPI. If your API is event-driven, use AsyncAPI. If you ship both (a public REST API that also emits Kafka events for downstream consumers), use both, side by side. They do not conflict. The Swagger vs OpenAPI distinction is a separate naming question that catches a lot of teams off guard, but it is unrelated to the AsyncAPI choice.
A common follow-up question: what about GraphQL vs REST? GraphQL has subscriptions, which are async, but GraphQL has its own schema language (SDL) that handles its needs. AsyncAPI is not the right tool for a pure GraphQL stack.
Tooling: what you actually run
AsyncAPI's tooling story has improved a lot since 2.x. The pieces you will end up using:
AsyncAPI CLI is the command-line entry point. It validates documents, runs the generator, bundles multi-file specs into one, and starts a local studio. Install with npm install -g @asyncapi/cli.
AsyncAPI Studio is the browser-based editor. You can paste a spec at studio.asyncapi.com and get live validation, a visual diagram of your channels, and a rendered HTML preview. It is the AsyncAPI equivalent of Swagger Editor.
AsyncAPI Generator turns a spec into something useful: HTML docs, Markdown, Java/Go/Python/TypeScript code stubs for producers and consumers, or a custom template you write yourself. Same idea as OpenAPI Generator, smaller catalog.
Bindings packages are JSON Schema files that validate protocol-specific fields. You import the relevant binding (asyncapi-kafka, asyncapi-amqp, etc.) when your spec uses protocol-specific config.
That is the official toolkit. Beyond it, several commercial doc platforms (Bump, Redocly, Stoplight, Microcks) accept AsyncAPI input and render polished portals.
Who should adopt AsyncAPI?
AsyncAPI earns its place when the cost of misaligned contracts between services is real. The teams that benefit most:
- Microservice teams using Kafka or RabbitMQ. When ten services publish to the same topic and a schema change breaks four consumers, an AsyncAPI contract in CI catches it before deploy.
- IoT platforms. MQTT topic structures and payload formats are notoriously poorly documented. A spec turns "ask the firmware lead" into a file in the repo.
- Real-time products with WebSocket APIs. Trading apps, chat apps, live dashboards. The message catalog is the API; documenting it as prose is fragile.
- Public event APIs. Stripe's webhook events, Slack's event API, Shopify's webhook topics. Anyone documenting webhook events for external developers gains from a spec consumers can validate against.
- Platform teams running an event mesh. The catalog of "what events exist" becomes the platform's most-read documentation. AsyncAPI plus a portal beats a wiki page every time.
It is overkill for: a single service with two internal queue messages, a side project, or a team where REST and OpenAPI already cover everything.
Where AsyncAPI is still rough
Honesty matters here because the spec is younger than OpenAPI and the gaps are real:
- Tooling ecosystem is smaller. OpenAPI has thousands of integrations; AsyncAPI has dozens. If your IDE has an OpenAPI plugin, the AsyncAPI plugin (if it exists) is usually less polished.
- Mock servers are weaker. Microcks does a good job for AsyncAPI, but the equivalent of Prism for OpenAPI is not as mature.
- Documentation rendering varies. The default HTML template is functional but plain. You usually want a doc platform on top of it (this is where most teams plug in something like Docsio or a competitor) rather than shipping the raw render.
- Adoption inside organizations is uneven. REST teams might already have OpenAPI conventions, but the messaging team is often documenting in Confluence. Rolling out AsyncAPI is half technical, half cultural.
If you can live with those caveats, the upside of having a single contract for your async surface is large. If your async surface is small, you can probably wait another year and let the ecosystem mature.
How AsyncAPI fits into your docs site
A spec file is a contract, not a developer experience. The spec tells you that user.signups.v1 carries a UserSignedUp payload with three required fields. It does not tell a new engineer when to subscribe vs poll, what the retry semantics look like, why partitioning is keyed on userId, or what happens when a downstream consumer is down for an hour.
Documentation does. Most teams that adopt AsyncAPI end up with a docs site that has two layers: the auto-generated reference (channels, messages, schemas) sitting next to handwritten guides (architecture overview, sequence diagrams, retry policies, sample consumer code). Docsio handles the second layer, the human-readable guide, the same way it does for REST and SDK docs: paste your URL, get a branded site, edit with an AI agent, and embed your AsyncAPI reference alongside your OpenAPI specification example and SDK docs in one portal. The reference comes from the spec; everything else comes from your team's actual experience operating the system.
FAQ
Is AsyncAPI the same as OpenAPI?
No. OpenAPI describes synchronous REST APIs (request and response over HTTP). AsyncAPI describes asynchronous, event-driven APIs (pub/sub over Kafka, MQTT, AMQP, WebSocket, NATS, and similar protocols). The two specs share a similar structure and are designed to coexist when a service exposes both styles.
What replaced Swagger?
Nothing replaced Swagger. The Swagger Specification was renamed to the OpenAPI Specification in 2015, and the Swagger brand stayed on the tooling (Swagger UI, Swagger Editor). For event-driven APIs, AsyncAPI fills a gap that Swagger and OpenAPI never covered, but it is a separate spec, not a replacement.
Which version of AsyncAPI should I use?
Use AsyncAPI 3.0.0 for new specs. It separated operations from channels and removed several ambiguities from 2.x. AsyncAPI 2.6 is still widely supported by tooling for legacy specs, but new work should target 3.0.
Does AsyncAPI work with REST APIs?
It can describe an HTTP-based async pattern such as Server-Sent Events or long-polling, but it is not the right tool for ordinary REST. For request-response over HTTP, use OpenAPI. The two specs are designed to live together when an API exposes both surfaces.
What is the difference between CloudEvents and AsyncAPI?
CloudEvents defines a common envelope format for event metadata (id, source, type, time). AsyncAPI describes the broader contract: which channels exist, what messages flow through them, and what each payload looks like. Many teams use both. CloudEvents inside the message envelope, AsyncAPI to describe the channels and message catalog.
