How-to › Document and support developers

How to add sequence diagrams to reference docs with Mermaid#

Write an OAuth exchange or a webhook round trip as a Mermaid sequence diagram beside the reference page, check it against the spec in CI, and render it in dark mode.

Audience
DevRel
Level
beginner
Topic
Publish reference docs
Verified

Your OAuth guide explains the code exchange in four paragraphs, but readers still post the code to the token endpoint from the browser. The one picture that would have shown the order of the calls is a PNG somebody drew two years ago. Nothing in the build knows that the PNG still shows an endpoint the last release renamed.

What you get

You will end up with sequence diagrams that live beside the reference pages in version control. A CI check fails when a diagram names an operation the spec no longer has, and the renders read on a dark page. This is for you if you document an API with a flow of more than one call.

Short answer

Write the flow as a Mermaid sequence diagram in a fenced block beside the reference page, with an accessible title and description and a paragraph that says the same thing in words. Name the operationId in each message, and run a check in CI that fails when the spec no longer has that operation. Render the dark theme as well as the default, because a diagram themed for one page is unreadable on the other.

You will need

Node 22 or later, an OpenAPI 3.1 document, and a site generator that renders a mermaid fence. GitHub renders one in any markdown file, and the integrations list names the plugins for Docusaurus, MkDocs, Sphinx, and GitLab. The render step needs mermaid-cli and a Chromium it can start. Verified 2026-09-25 against Node 22.22.2, @mermaid-js/mermaid-cli 12.0.0, and yaml 2.9.1.

Approaches compared

ApproachWhen it fitsWhat it costs youWhen to pick something else
An exported image from a drawing toolA one-off figure that nobody will edit againA long text description you write and maintain by hand, and a diff that is a binary blobThe flow will change with the spec, which a multi-call flow does
D2You want a say in the layout, with a choice of three layout enginesA Go binary in the docs build, and SVG files you commit or generate because no site generator renders it nativelyYour site renders Mermaid fences already and the flows are short
MermaidDocs on GitHub or a generator with a Mermaid plugin, and flows short enough to need no layout controlThe least control over layout of the three, and colors that belong to the theme rather than to your pageLayout matters more than toolchain, or the flow has dozens of messages
PlantUMLA team with Java in the build already, and a house set of skinparam settings to applyJava in the docs build, Graphviz for some diagram types, and PNG output unless you ask for SVGNobody wants a JVM in the documentation pipeline

Mermaid asks for nothing your site does not already have, and gives you the least say over where things go. D2 and PlantUML each put a binary in the docs build and hand you layout engines or skin parameters in return. The drawn image costs the most over time, because nothing in the build can tell you when the spec moves and the picture does not.

Write the flow beside the page, naming the operation in each message

The diagram is text in the same markdown file as the prose, so a pull request that changes the flow shows the change as lines. Each message that corresponds to an API call ends with the operationId in parentheses.

sequenceDiagram
  accTitle: Authorization code exchange
  accDescr: The browser is sent to authorize, comes back to the client with a code, the client exchanges the code for a token at the token endpoint, and calls the Orders API with it.
  participant B as Browser
  participant C as Client
  participant A as Authorization server
  participant O as Orders API
  B->>A: GET /oauth/authorize (authorize)
  A-->>B: 302 to the redirect URL with a code
  B->>C: GET /callback with the code
  C->>A: POST /oauth/token (exchangeToken)
  A-->>C: 200 with access_token and expires_in
  C->>O: GET /orders (listOrders)
  O-->>C: 200 with the orders

Three things in that block do work beyond drawing. accTitle and accDescr become a title and a desc element inside the SVG, and Mermaid points aria-labelledby and aria-describedby at them, which its accessibility page shows in full. The operationId in parentheses is what the CI check reads. And the paragraph after the fence says the flow in words, for the reader with images turned off and the agent that never loads them.1

The webhook diagram is the same shape. Its orderCreated message cites an operation that lives under the document’s webhooks key rather than under paths, and the checker reads both.

Check every diagram against the spec

No linter reads pictures, so a diagram goes on contradicting the spec from the day an endpoint is renamed. The check parses every mermaid fence under docs, and holds each cited operation to the document. A run that finds no fences fails, so a CI step pointed at the wrong directory cannot pass forever.

  for (const msg of seq.messages) {
    if (!msg.id) continue
    const op = ops.get(msg.id)
    if (!op) {
      findings.push({ kind: 'unknown-operation', detail: `${at(msg.line)} names ${msg.id}, which is not in the spec` })
      continue
    }
    if (msg.method && msg.method !== op.method) {
      findings.push({ kind: 'wrong-method', detail: `${at(msg.line)} says ${msg.method} and the spec says ${op.method} for ${msg.id}` })
    }
    if (msg.path && op.kind === 'path' && msg.path !== op.path) {
      findings.push({ kind: 'wrong-path', detail: `${at(msg.line)} says ${msg.path} and the spec says ${op.path} for ${msg.id}` })
    }
  }

Against the release the diagrams were written for, everything matches.

node check-diagrams.mjs docs openapi.yaml
2 sequence diagrams under docs, against openapi.yaml 3.2.0
  docs/oauth-exchange.md:7  "Authorization code exchange"  7 messages, 3 cite an operation
  docs/webhook-round-trip.md:7  "Webhook round trip"  6 messages, 3 cite an operation
  every cited operation matches the spec

Release 4.0.0 renames exchangeToken to issueToken and moves listOrders to /v2/orders. The same command against that document is the pitfall, caught.

node check-diagrams.mjs docs openapi-next.yaml
2 sequence diagrams under docs, against openapi-next.yaml 4.0.0
  docs/oauth-exchange.md:7  "Authorization code exchange"  7 messages, 3 cite an operation
    unknown-operation  docs/oauth-exchange.md:17 names exchangeToken, which is not in the spec
    wrong-path         docs/oauth-exchange.md:19 says /orders and the spec says /v2/orders for listOrders
  docs/webhook-round-trip.md:7  "Webhook round trip"  6 messages, 3 cite an operation
  2 findings

Each finding carries the file and line of the message, so the fix is an edit to one line of the diagram, in the same pull request as the spec change. The checker also refuses a diagram with more than twelve messages, because a long flow is the one that drifts first and the one nobody rereads. Split it, and give each half a page.

Render it for a dark page as well as a light one

Mermaid’s themes color the text, and the page colors the background, so a render made for one page can be invisible on the other. render.mjs draws every diagram twice with mermaid-cli, in the default theme and the dark theme, and writes the SVG files under rendered. It needs a Chromium to start, so it runs on a workstation and the files are committed.

node render.mjs

The inspection reads the committed files. It takes the background the page will paint and computes the contrast of the message text against it, as WCAG 2.1 defines the ratio. The dark value here is this site’s own code panel.

node inspect-svg.mjs '#101018' rendered/oauth-exchange.dark.svg rendered/oauth-exchange.default.svg
rendered by @mermaid-js/mermaid-cli 12.0.0
page background #101018
  oauth-exchange.dark.svg
    role sequence, title "Authorization code exchange", description 33 words
    text lightgrey on #101018: contrast 12.6:1, readable
  oauth-exchange.default.svg
    role sequence, title "Authorization code exchange", description 33 words
    text #333 on #101018: contrast 1.5:1, below the 4.5:1 floor

The default theme’s text is #333, which is 1.5:1 on a dark panel, and the ratio reverses to the decimal on white. Serve both renders and let the browser choose with a picture element and a prefers-color-scheme media query, or let the site’s Mermaid plugin pick the theme at render time. Do not fix the theme inside the diagram unless the site has one color scheme, because the diagram’s own frontmatter wins over every other setting.2

Check it worked

Twenty tests pin the checker and the inspection. Two carry the page: the diagrams match release 3.2.0 and contradict 4.0.0 on unknown-operation and wrong-path, and the dark render is readable on the dark panel while the default render is not.

node --test diagrams.test.mjs
1..20
# tests 20
# suites 0
# pass 20
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 255.037197

Open the page with images turned off, or read its markdown twin. The paragraph that starts In words: is the diagram, and if it is missing the checker reports no-prose for that fence.

When it goes wrong

The checker reports an unknown operation and the diagram is right. The operationId in the parentheses has a typo, or the operation is a webhook and the document keeps it somewhere other than the top-level webhooks key. Copy the id from the spec rather than from memory.

The site shows the diagram source as a code block. The generator has no Mermaid support, or the plugin is installed and not enabled. The integrations list names the plugin for each generator, and GitHub needs nothing.

The diagram fails to render and the source looks fine. A message or a note contains the word end, which the sequence grammar reserves.3 Wrap it in parentheses, quotation marks, or brackets.

The render is fine on the workstation and unreadable on the site. The site switched to dark mode and the diagram carries a fixed theme in its frontmatter. Remove it, and let the plugin or the picture element choose.

When not to do this

Do not diagram a single request and its response. A sequence diagram earns its place when the order of three or more calls is the thing readers get wrong, and a two-line flow is better as a sentence.

Do not put the whole API in one diagram. The checker’s ceiling of twelve messages is not a limit of the tool, it is the point past which nobody reads the picture and nobody updates it. One flow per diagram, one diagram per page.

Do not let the picture be the only description. An agent reading the markdown twin gets the fence’s source text, which reads as code, and a reader with images turned off gets nothing. The paragraph in words is the diagram for both of them.

Do not reach for D2 or PlantUML to get layout control you have not yet needed. Both cost a binary in the docs build. The flows on a reference page are short enough that Mermaid’s layout is fine until it is not, and that is the day to switch.

Last verified

Verified 2026-09-25 against Node 22.22.2, @mermaid-js/mermaid-cli 12.0.0, and yaml 2.9.1. Every output block is what the command preceding it printed. The SVG files under rendered were drawn by node render.mjs on this machine with a headless Chromium, and the manifest beside them records the tool. CI reads the committed files and starts no browser.

Footnotes

  1. The W3C’s tutorial on complex images asks for a two-part text alternative. The short description identifies the image and says where the long one is, and the long description carries the essential information. It offers three ways to attach the long one. The third wraps the image and its description together in an HTML5 figure, so the description is text on the page beside the picture. A reader who never sees the image gets the same sentences as one who does. ↩︎ Back to text

  2. Mermaid’s theming page lists the order in which a setting wins. The diagram’s own frontmatter or %%{init}%% directive comes first, then what was passed to initialize(), then the diagram type’s default, then the global default. Since version 12.0.0 a sequence diagram’s own default is the redux-color theme with the neo look, and the page says that the most specific thing you say wins. A theme written into the diagram is the most specific thing anyone can say. ↩︎ Back to text

  3. The sequence diagram syntax opens with a warning that the word end could break the diagram, due to the way that the Mermaid language is scripted. If unavoidable, it must be enclosed in parentheses, quotation marks, or brackets. A grammar for describing the ends of things has one word it cannot say plainly, and its documentation puts that first. ↩︎ Back to text

Read this page as markdown · All how-to guides

Generate the client instead of writing it#

Retries, timeouts, pagination and auth are the same problems in every client. Voxgig generates them from your OpenAPI description, in 23 languages, from one model.

Get the Voxgig dispatch

Short notes on building SDKs, CLIs, REPLs, and MCPs for API-first teams, plus the occasional Fireside episode pick.

By signing up you agree to our Terms and Conditions.