Event-driven APIs ship without usable reference documentation far more often than REST APIs do. The spec exists, the WebSocket channel works, and the only artifact a developer gets is an asyncapi.yaml file in a repo plus a README paragraph describing the handshake. An AsyncAPI documentation generator closes that gap, but the tools in this category differ enormously in what they actually produce. The decision axis: choose a generator that treats the AsyncAPI document as a build input to your whole developer surface, not one that renders it as a static viewer page. A viewer tells developers what messages exist. A build pipeline gives them a typed client, validated examples, and a reference that cannot drift from the spec.
TLDR:
- An AsyncAPI documentation generator parses an AsyncAPI document and renders channels, operations, messages, and protocol bindings as browsable reference documentation.
- The specification's current line is 3.x, which separates
operations(sendandreceive) fromchannels, making bi-directional WebSocket and pub/sub flows explicit rather than implied. - The open-source landscape covers rendering well and clients poorly: the AsyncAPI Generator, EventCatalog, Bump.sh, and Modelina each solve one slice of the problem.
- Generated event-driven docs consistently omit the parts developers get stuck on: connection lifecycle, auth handshake, reconnection semantics, and error frames. Those need authored prose alongside the generated reference.
- Validating the spec in CI is the difference between documentation that stays accurate and documentation that quietly rots after the third schema change.
- Fern generates a WebSocket API reference and typed client libraries from the same AsyncAPI document, supporting AsyncAPI 2.6.0 and 3.0.0.
What is an AsyncAPI documentation generator?
An AsyncAPI documentation generator reads an AsyncAPI document and emits human-readable reference documentation for the event-driven API it describes. AsyncAPI is the specification for asynchronous and event-driven APIs, covering WebSockets, webhooks, and message brokers like Kafka, MQTT, and AMQP, and it serves the role for those protocols that OpenAPI serves for REST.
The generator's job is narrower than it sounds. It resolves $ref pointers, walks the channel and operation tree, expands message payload schemas, and renders each message shape with its examples, headers, and content type. What separates generators is what they do beyond rendering: some also emit client code, some emit architecture diagrams, some publish to a hosted site with change tracking, and some stop at a single static HTML file.
That difference matters because event-driven APIs put more of the integration burden on the client. A developer consuming a REST endpoint needs a URL, a method, and a payload shape. A developer consuming a WebSocket channel needs all of that plus a connection sequence, an authentication handshake, a subscription model, ordering guarantees, and a reconnection strategy. Documentation that renders only the message catalog leaves most of that unanswered.
What an AsyncAPI document actually describes
What a generator has to work with explains why output quality varies so widely. AsyncAPI 3.x organizes the document into a small number of top-level objects.
| Object | What it defines | Why it matters for generated docs |
|---|---|---|
info | Title, version, description, contact, license | Becomes the reference landing page and version label |
servers | Host, protocol, protocol version, security | Tells developers where to connect and over which transport |
channels | Addresses where messages flow, plus parameters | The navigational spine of the generated reference |
operations | send and receive actions bound to channels | Distinguishes what the client does from what the server does |
messages | Payload schemas, headers, contentType, examples | The part developers copy into their code |
components | Reusable schemas, messages, servers, security schemes, traits, bindings | Determines whether the generator produces clean shared types or duplicated inline shapes |
bindings | Protocol-specific settings per server, channel, operation, or message | Carries the WebSocket, Kafka, and MQTT detail a transport-agnostic renderer drops |
The 3.x split between channels and operations is the structural change worth understanding. In AsyncAPI 2.x, a channel carried publish and subscribe keys, and the perspective of those verbs was a long-standing source of confusion. In 3.0.0, operations became top-level objects with an explicit action of send or receive that reference a channel via $ref. Bi-directional communication is expressed by defining both a send and a receive operation against the same channel, which is exactly the shape a full-duplex WebSocket connection has. Generators that model this correctly show both halves of the conversation on one page.
Bindings are the object most commonly under-rendered. A WebSocket binding carries the query parameters and headers used during the upgrade request; a Kafka binding carries the group ID and client ID; an MQTT binding carries QoS and retain flags. A generator that renders the payload schema and ignores bindings produces documentation that looks complete and fails on the first connection attempt.
How AsyncAPI documentation generation works
Every generator runs roughly the same pipeline, and knowing the stages tells you where a given tool will fall down.
- Parse and validate. The document is checked against the AsyncAPI JSON Schema for its declared version. The AsyncAPI CLI exposes this through
asyncapi validate, and Redocly CLI lints AsyncAPI documents with rules such aschannels-kebab-caseandoperation-operationId, each settable toerror,warn, oroff, though Redocly describes its AsyncAPI support as early stage. - Resolve references. Internal and external
$refpointers are dereferenced into one in-memory document. Channel references use JSON Pointer notation with URL encoding, so#/channels/user~1notificationsresolves the channel addresseduser/notifications. - Build an intermediate model. The resolved document becomes a traversable model of servers, channels, operations, and messages, with schemas flattened or preserved depending on the generator.
- Render through a template. The model is passed to a template that emits HTML, Markdown, diagrams, or code.
- Publish. The artifact is deployed as static files or through a hosted platform with versioning and change tracking.
Steps 1 and 5 decide documentation quality over time. A generator invoked by hand produces accurate docs exactly once. A generator wired into CI, validating every pull request that touches the spec, produces documentation that stays accurate for as long as the API lives.
AsyncAPI documentation generators to evaluate in 2026
The tooling landscape splits cleanly into renderers, catalogs, hosted platforms, and code generators. Most teams end up combining two or three.
AsyncAPI Generator
The official AsyncAPI Generator is a template-driven engine invoked through the AsyncAPI CLI: asyncapi generate fromTemplate asyncapi.yaml @asyncapi/html-template. Two templates cover documentation. The HTML template produces a static site and uses the AsyncAPI React component under the hood. The Markdown template produces Markdown that can sit alongside code in a repo or feed an existing static-site pipeline. Templates also exist for code and configuration, and custom templates are supported. It is the most flexible option and the one that requires the most assembly.
EventCatalog
EventCatalog approaches the problem from architecture rather than reference documentation. It ingests AsyncAPI specifications and generates a browsable catalog of events, channels, services, and domains, plus architecture diagrams, ownership assignment, automatic versioning, and a visualizer showing how those entities relate. For an organization with dozens of services publishing to shared brokers, this discovery layer answers a question a per-spec reference cannot: who produces this event, and who consumes it.
Bump.sh
Bump.sh is a hosted documentation platform that publishes portals from both OpenAPI and AsyncAPI documents, with a CLI and a GitHub Action that deploy new versions on every push. Its distinguishing feature for event-driven work is automated changelog and versioning between spec versions, which gives consumers a record of when a message schema changed.
Modelina
Modelina is not a documentation generator, but it fills the adjacent gap: typed data models from AsyncAPI, OpenAPI, and JSON Schema inputs across a wide language set including TypeScript, Java, C#, Go, Python, Rust, Kotlin, and Dart. Teams pair it with a documentation renderer to get message payload types without hand-writing them.
Fern
Fern generates a WebSocket API reference and typed client libraries from the same AsyncAPI document, with support for AsyncAPI 2.6.0 and 3.0.0. It reads WebSocket, MQTT, Kafka, and HTTP protocol bindings, and AsyncAPI references sit in the same navigation as MDX guides and OpenAPI references, so a team with both a REST surface and a WebSocket surface publishes one site rather than two.
AsyncAPI vs OpenAPI: what changes when you document events
The specifications look similar; the documentation problems are not. OpenAPI describes a request-response transaction: one call, one response, stateless. AsyncAPI describes a conversation over a persistent connection, where either side can speak first and messages arrive without being requested.
| Concern | OpenAPI / REST | AsyncAPI / event-driven |
|---|---|---|
| Unit of documentation | Endpoint (path plus method) | Channel plus operations |
| Direction | Client requests, server responds | Either side sends; often both |
| State | Stateless per request | Connection state, subscriptions, sequence |
| Transport | HTTP | WebSocket, Kafka, MQTT, AMQP, SSE, webhooks |
| Interactive testing | Straightforward: replay a request | Hard: requires a live connection and a message sequence |
| Failure modes | Status codes | Error frames, disconnects, redelivery, ordering |
The interactive-testing row is the practical one. A REST reference can offer a "try it" panel that fires a request and shows the response. An event-driven reference cannot do that without opening a socket, authenticating, subscribing, and holding the connection. Good AsyncAPI documentation compensates with better examples: complete message sequences showing the frames exchanged in order, not isolated payload schemas.
AsyncAPI also rarely stands alone. Most APIs with a WebSocket surface also have a REST surface, documented from a second specification, and publishing them as two sites forces developers to learn two navigations for one product. This is the same coupling problem that shows up when REST and gRPC live in separate references, and it has the same solution: one site, one navigation, multiple spec inputs.
What generated event-driven documentation leaves out
Running a generator produces a message catalog. It does not produce integration documentation. These are the gaps a practitioner hits, in roughly the order they hit them.
The connection lifecycle. The servers object gives a host and protocol. It does not say whether the client must send a subscription frame after connecting, whether the server sends a ready or session message first, or how long the handshake takes before messages flow. This is the first thing a developer needs and it is almost never in the generated output.
The authentication handshake. AsyncAPI models security schemes including bearer, basic, and API key, but where the credential goes on a WebSocket connection is protocol detail: a query parameter on the upgrade URL, an Authorization header, or a token in the first message frame. Each is legitimate and each requires different client code. Document which one, explicitly.
Reconnection and resumption. Persistent connections drop. Whether the client resumes from a cursor, replays from a sequence number, or reconnects fresh and accepts a gap is a design decision with no representation in the specification. Undocumented, every consumer invents a different reconnection strategy and half of them silently lose messages.
Error frames and close codes. Event-driven APIs signal failure through error messages on the channel and WebSocket close codes, not HTTP status codes. Both belong in the reference. Close codes are frequently custom, and an undocumented 4003 close is a support ticket.
Ordering and delivery guarantees. At-least-once versus at-most-once, per-channel versus global ordering, and whether duplicates are possible determine whether a consumer needs idempotency handling. These are prose facts, not schema facts.
Backpressure. If the server produces faster than a slow consumer reads, something has to give. Whether the server buffers, drops, or disconnects is a documented contract or an outage.
Treat the generated reference as the schema layer and author a "connecting to the API" guide beside it covering these six concerns. Generators that keep MDX or Markdown guides in the same navigation as the generated reference make that pairing natural; generators that emit a standalone HTML file make it a separate site.
How to document an AsyncAPI in practice
A workable pipeline for documenting an event-driven API looks like this.
- Write the spec first, not after. Define channels, operations, and message schemas before the implementation lands. A spec-first workflow makes the documentation and the client derivable from the contract rather than reverse-engineered from the broker.
- Model bi-directional channels explicitly. For a full-duplex WebSocket, define both a
sendand areceiveoperation against the same channel so the reference shows both halves. - Fill in bindings. Add WebSocket, Kafka, or MQTT bindings for every server and channel. This is the transport detail that makes generated docs actionable rather than theoretical.
- Add real examples to every message. A schema with no example forces developers to construct a payload from types. Add
examplesentries with realistic values, and prefer a sequence of frames over a single isolated payload. - Validate and lint in CI. Run
asyncapi validateor a Redocly rule set on every pull request touching the spec. Broken references and missingoperationIds should fail the build, not the docs deploy. - Regenerate on merge, never by hand. Wire generation into the pipeline so the published reference is a function of the merged spec. Manual regeneration is how documentation drifts from behavior.
- Author the integration guide alongside it. Cover the six gaps above in prose that lives in the same site as the generated reference.
- Serve the raw spec. Publishing the machine-readable document lets consumers generate their own clients and lets AI tools read the contract directly.
Generating clients, not just pages, from the same document
The argument for coupling SDK generation to documentation generation is stronger for event-driven APIs than for REST. Hand-written WebSocket client code carries more incidental complexity than an HTTP call: socket setup, heartbeat handling, message dispatch by discriminator, typed payload deserialization, reconnection with backoff. Every consumer writes that code, most write it slightly wrong, and the support burden falls on the API provider.
A generated client collapses the message catalog into typed methods and typed event handlers, which changes what the documentation has to carry. When a developer works against a typed client, the reference no longer teaches payload construction; the type system does that in the editor. The reference's job narrows to semantics: what this message means, when it arrives, and what to do about it.
The requirement this creates is a single source of truth. If the documentation renders from one copy of the spec and the client generates from another, the two diverge on the next schema change, and a typed client that disagrees with the reference is worse than no client at all. Both artifacts have to build from the same document in the same pipeline. That is the coupling argument for client libraries in a docs-as-code workflow generally, and it binds harder here because the client is doing more work.
Documenting AsyncAPI with Fern
Fern reads an AsyncAPI document as an API definition and produces both a WebSocket API reference and typed client libraries from it, currently supporting AsyncAPI 2.6.0 and 3.0.0 with SDK generation in TypeScript, Python, Java, C#, and Rust. AsyncAPI-derived references live in the same navigation as MDX guides and OpenAPI references, so the integration guide sits next to the generated channel reference rather than on a separate site. A set of x-fern extensions adjusts the generated output without forking the spec: x-fern-examples adds message examples, x-fern-audiences filters operations and channels by audience, x-fern-sdk-method-name renames generated methods, and x-fern-ignore excludes internal channels. Docs sites serve the raw AsyncAPI 2.6.0 spec so consumers and AI tools can read the contract directly.
Final thoughts on choosing an AsyncAPI documentation generator
The rendered page is the least differentiated part of an AsyncAPI documentation generator. Every tool in the category turns channels and messages into readable HTML. What separates them is whether the generator is a viewer or a build step: whether it validates in CI, regenerates on merge, keeps the raw spec available, and produces the typed client developers integrate against from the same document. Pick for the pipeline, not the theme, and pair whatever you pick with an authored guide covering connection lifecycle, auth handshake, reconnection, and error frames, because no generator infers those from a schema.
Book a demo to see how Fern generates WebSocket references and typed clients from one AsyncAPI document.
FAQ
What is the difference between AsyncAPI and OpenAPI?
OpenAPI describes synchronous request-response APIs over HTTP, where a client calls an endpoint and receives a response. AsyncAPI describes event-driven and asynchronous APIs, covering WebSockets, webhooks, and message brokers such as Kafka, MQTT, and AMQP. The object models differ accordingly: OpenAPI is organized around paths and methods, while AsyncAPI is organized around channels, operations, and messages. Most products with both a REST surface and a streaming surface maintain both specifications.
Is there a free, open-source AsyncAPI documentation generator?
Yes. The AsyncAPI Initiative maintains the AsyncAPI Generator, invoked through the AsyncAPI CLI, with official HTML and Markdown templates for documentation output. EventCatalog and Modelina are also open source, covering architecture catalogs and typed model generation respectively. The tradeoff with the open-source path is assembly: hosting, versioning, search, and CI wiring are left to the team adopting them.
How do you document a Kafka API with AsyncAPI?
Define each Kafka topic as a channel, model producing and consuming as send and receive operations, put the event payload schemas in components.messages, and add Kafka bindings to carry the group ID, client ID, and partition detail. Because Kafka topics are usually shared across many services, a catalog view showing which service produces and consumes each event is often more valuable than a per-spec reference, which is the gap EventCatalog fills.
Can you generate SDKs from an AsyncAPI specification?
Yes, though language coverage is narrower than for OpenAPI. Fern generates typed client libraries from AsyncAPI in TypeScript, Python, Java, C#, and Rust. Modelina generates data models, rather than full clients, from AsyncAPI documents across a broader language set. Generating the client and the reference from the same specification is what prevents the two from disagreeing after a schema change.
Which AsyncAPI version should a new event-driven API target?
Target the 3.x line. The 3.0.0 release moved operations to the top level with explicit send and receive actions, replacing the publish and subscribe keys whose perspective was a persistent source of confusion in 2.x, and 3.1.0 is the current release. Confirm your chosen generator supports 3.x before committing, since some tooling still targets 2.6.0 only, and check whether it reads the protocol bindings your transport requires.