API StudioSDK StudioMonitoringMCP ServerSpec AuditFeatures
ComparePricingBlogDocs
Log inStart for free
API StudioSDK StudioMonitoringMCP ServerSpec AuditFeatures
ComparePricingBlogDocs
Log inStart for free
API StudioSDK StudioMonitoringMCP ServerSpec AuditFeatures
ComparePricingBlogDocs
Log inStart for free
Blog/Guides
Guides·September 11, 2026·5 min read

Documenting webhooks in OpenAPI 3.1, and the half it cannot describe

3.1 finally gave webhooks a home in the spec. It describes the payload, which is the easy part, and says nothing about delivery, which is where integrations break.

#webhooks#openapi#api-design#api-documentation
Documenting webhooks in OpenAPI 3.1, and the half it cannot describe

Before 3.1, webhooks had no natural place in an OpenAPI document. Teams either bolted them into paths as endpoints they did not serve, or documented them in prose beside the spec and let them drift.

3.1 added a top-level webhooks keyword, and it is a genuine improvement. It is also commonly mistaken for solving the whole problem, when it solves the smaller half.

What the keyword does

webhooks sits at the root, beside paths, and describes requests your API sends rather than requests it receives:

yaml
1234567891011121314
openapi: 3.1.0
webhooks:
  invoicePaid:
    post:
      summary: Sent when an invoice moves to paid.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InvoicePaidEvent'
      responses:
        '2XX':
          description: Acknowledged. Any 2xx stops redelivery.

The inversion is the point. The requestBody is what you send to the consumer, and the responses are what you expect back from them. Reading it as an endpoint you serve gets the direction backwards.

The immediate payoff is that consumers can generate types for your event payloads, which is the thing they most want and most often hand-write from a documentation table.

Webhooks and callbacks are not the same thing

Both exist, they look similar, and the distinction is worth getting right because it changes where the definition goes.

Callbacks are attached to an operation. They describe a request your API makes as a consequence of that specific call, to a URL supplied in that call. A job submission that takes a callbackUrl and posts back when the job finishes is a callback.

Webhooks are not attached to any operation. They are events your API emits based on things happening in your system, to a URL configured out of band, usually in a dashboard.

If the destination came from a field in a request, it is a callback. If it came from settings, it is a webhook.

Design the event envelope once

The most useful decision is one the spec will not prompt you to make: give every event the same outer shape, with the type and the payload separated.

json
123456
{
  "id": "evt_01HQ...",
  "type": "invoice.paid",
  "created": "2026-10-29T09:12:04Z",
  "data": { }
}

That envelope is what lets a consumer write one handler that switches on type, and it is what lets you add event types without every consumer changing their parsing. Four fields earn their place:

id, because deduplication needs a stable key and this is it. type, dotted and stable, because it is the thing consumers branch on and renaming one breaks every handler. created, because out-of-order delivery is normal and consumers need to know which of two events is newer. data, because keeping the payload in its own object is what stops a new envelope field colliding with a payload field.

The half OpenAPI does not describe

Here is the part that decides whether an integration works, and none of it is expressible in the spec. It belongs in a written guide, and it is the guide most teams do not have.

Signature verification. Which header carries the signature, what exactly is signed (the raw body, and whether a timestamp is included), which algorithm, and how to rotate a secret. The critical detail people get wrong: consumers must verify against the raw body, before any JSON parsing, because re-serialising changes the bytes and the signature will not match. If your framework parses the body before your handler sees it, say so, because that is the single most common integration failure.

Retry schedule. How many attempts, over what window, with what backoff. What counts as success, which should be any 2xx and nothing else. Whether a 410 stops redelivery permanently.

Ordering. Almost certainly not guaranteed. Say it plainly, because consumers assume ordering by default and build state machines that depend on it.

Delivery semantics. At-least-once, which means duplicates. Combined with the id field, that gives consumers what they need to be idempotent, and you should show that pattern rather than describing it.

Timeout. How long you wait for their acknowledgement before treating it as failed. Consumers need this to decide whether to process inline or enqueue and return immediately. The right advice is almost always enqueue and return.

IP ranges or a static egress, if you have one, because some consumers need it for their firewall.

Testing is part of the contract

A webhook integration cannot be developed without a way to trigger events on demand. If your dashboard has no "send a test event" button, every consumer builds a workaround, usually by making real objects in your system to force real events.

Alongside it: a delivery log showing what you sent, what came back and when you retried. This is what turns "your webhooks are not working" into a resolvable conversation, and its absence turns every delivery question into a support ticket that only you can answer.

Where this leaves the document

Put the event schemas in webhooks. Consumers get generated types and your reference page gets a section it did not have.

Then write the delivery guide, because the schema tells someone what the JSON looks like and the guide tells them how to build a handler that is correct under retries, duplicates and reordering. The first is generated. The second is the kind of page that has to be written, alongside your errors page and your auth flow.

What belongs in API docs that OpenAPI cannot generate covers the rest of that set and how to decide what goes in each. If your spec is not on 3.1 yet, the spec audit will tell you what else is holding your generated output back, free and without an account.

← PreviousAPI-first in practice: what changes when the spec is the source of truth

Related articles

API-first in practice: what changes when the spec is the source of truth
Guides·5 min read

API-first in practice: what changes when the spec is the source of truth

The phrase is used to mean four different things. Only one of them changes how your team works, and it is the one that makes the spec a build input rather than a report.

September 10, 2026
Getting your API docs cited when someone asks an AI about your product
Guides·6 min read

Getting your API docs cited when someone asks an AI about your product

People are asking assistants what your API does before they visit your site. What gets quoted back is decided by how your docs are structured, not by a file at your root.

September 8, 2026
Publishing SDKs to npm, PyPI, Maven Central and the rest
Guides·5 min read

Publishing SDKs to npm, PyPI, Maven Central and the rest

Seven registries, seven sets of rules, and two languages that have no registry at all. The decisions you cannot take back are all made before the first release.

September 8, 2026
Your MCP server needs documentation, and it is not your API docs
Guides·5 min read

Your MCP server needs documentation, and it is not your API docs

There is no spec format for MCP servers yet, so what an integrator gets is a README and a config snippet. Here is what actually has to be in it.

September 7, 2026

A letter when something ships

New SDK languages, changes in the generator, and now and then a longer piece on keeping docs from rotting. Roughly one a month.

Join developers keeping tabs on Octri.

Octri

Upload an OpenAPI spec. Get complete docs and production-ready SDKs in 10 languages, live in minutes.

Contact support

Product

  • API Studio
  • SDK Studio
  • Monitoring
  • MCP Server
  • Pricing
  • Compare
  • Blog
  • Changelog
  • Press Kit

From your spec

  • Spec Audit
  • TypeScript SDK
  • Python SDK
  • Go SDK
  • Java SDK
  • MCP Server

Developers

  • Documentation
  • API Reference
  • SDK Libraries
  • MCP Server
  • Monitoring
  • CLI
  • Support

Legal

  • Terms of Service
  • Privacy Policy
  • Fair Use Policy
  • Data Processing (DPA)
  • Cookie Policy
  • Security
  • Subprocessors

© 2026 Octri, LLC. All rights reserved.

Made by devs who got tired of hand-writing SDKs.