Background image

OpenAPI Tips

Webhooks & Callbacks in OpenAPI/Swagger

Nolan Di Mare Sullivan

Nolan Di Mare Sullivan

December 5, 2023

Featured blog post image

This article explains how to use callbacks (opens in a new tab) and webhooks (opens in a new tab) in OpenAPI.

What Are Webhooks and Callbacks and When Are They Used in OpenAPI?

A traditional REST API functions by allowing users to trigger a request that the API immediately sends a response to; this is known as a pull mechanism. Webhooks and callbacks expand the possible ways of working with the API beyond this traditional call/response interface.

  • webhooks are a mechanism that allows an API to send real-time data to a user as soon as an event occurs (without requiring the user to take any action). The user simply needs to subscribe to the event stream and provide a URL to start receiving data.
  • callbacks are a mechanism that allows a user to specify a URL to which an API request should send a certain response.

Which Should You Use?

Both webhooks and callbacks are a way of defining asynchronous communication with an API. You can use webhooks and callbacks somewhat interchangeably, but we recommend sticking to the following convention:

  • If users will be receiving a stream of data over time with a consistent format, you should use webhooks.
  • If the initial API request triggers a long-running job and the user wants to receive the response asynchronously, use callbacks.

A Short History of Webhooks and Callbacks in OpenAPI

Callbacks were added to the OpenAPI Specification in version 3 (opens in a new tab) in 2017; webhooks in version 3.1 (opens in a new tab) in 2021.

OpenAPI VersionDateNotes
3.0.0 (opens in a new tab)2017-07Callbacks added
3.0.1 (opens in a new tab)2017-12
3.0.2 (opens in a new tab)2018-10
3.0.3 (opens in a new tab)2020-02
3.1.0 (opens in a new tab)2021-02Webhooks added

In OpenAPI, webhooks and callbacks are defined as follows:

  • webhooks are a top-level entry represented by a map of Path Item Objects or OpenAPI Reference Objects that are keyed by the unique name of the webhook. Webhooks specify what is pushed to a given URL but provide no way of setting the target URL. The subscription itself might be configured by a sales representative, entered during the sign-up process when a user creates an account on your system, or even set by a separate API endpoint (duplicating how callbacks work).
  • A callback is a map of runtime expressions (that represent a URL the callback request is sent to) to a Path Item Object or Reference that defines a request to be initiated by the API provider and a potential response to be returned. The expression, when evaluated at runtime, will resolve to a URL represented in the parameters, request body, or response body of the parent operation.

A valid API schema can comprise only webhooks. For your schema to be valid, it needs only an info element and at least one of paths, webhooks, or components.

Creating a Webhook in OpenAPI

Below is the same notification service described as a webhook.

Add a Webhook Description

Add a webhook named concertAlert that describes what information will be pushed to a subscriber. This is the only requirement for a valid schema with a webhook.

Notice that there is no way for a user to register for this alert using the API, nor is the URL on which the user will be notified specified.


openapi: 3.1.0
info:
title: SpeakeasyClub
version: 1.0.0
webhooks:
concertAlert:
post:
summary: Concert alert
description: Notify the registered URL with details of an upcoming concert
requestBody:
required: true
content:
text/plain:
schema:
type: string
example: "The Swing Machine will be playing at 19h30 tomorrow. $10 cover charge."
responses:
'200':
description: Notification received by the external service.

Add a Subscription Path

To allow users to register for alerts without using a callback, you can optionally mimic callback functionality by proving a subscription endpoint in a /path.

OpenAPI has no way to explicitly link the webhook with the registration. You will have to make this clear to users in your description elements or by grouping the operations with tags.


openapi: 3.1.0
info:
title: SpeakeasyClub
version: 1.0.0
webhooks:
concertAlert:
post:
summary: Concert alert
description: Notify the registered URL with details of an upcoming concert
requestBody:
required: true
content:
text/plain:
schema:
type: string
example: "The Swing Machine will be playing at 19h30 tomorrow. $10 cover charge."
responses:
'200':
description: Notification received by the external service.
paths:
/registerForAlert:
post:
summary: Register for concert alerts
description: Register a URL to be called when new concerts are scheduled.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
format: uri
description: The URL to be notified about approaching concerts.
example:
url: "http://example.com/notify"
responses:
'200':
description: Registration successful.

Add a Webhook Description

Add a webhook named concertAlert that describes what information will be pushed to a subscriber. This is the only requirement for a valid schema with a webhook.

Notice that there is no way for a user to register for this alert using the API, nor is the URL on which the user will be notified specified.

Add a Subscription Path

To allow users to register for alerts without using a callback, you can optionally mimic callback functionality by proving a subscription endpoint in a /path.

OpenAPI has no way to explicitly link the webhook with the registration. You will have to make this clear to users in your description elements or by grouping the operations with tags.


openapi: 3.1.0
info:
title: SpeakeasyClub
version: 1.0.0
webhooks:
concertAlert:
post:
summary: Concert alert
description: Notify the registered URL with details of an upcoming concert
requestBody:
required: true
content:
text/plain:
schema:
type: string
example: "The Swing Machine will be playing at 19h30 tomorrow. $10 cover charge."
responses:
'200':
description: Notification received by the external service.

Creating a Callback in OpenAPI

Let's create a simple callback example. We use YAML rather than JSON for readability.

Add a Subscription Path

Now add a single /registerForAlert endpoint that a user can pass a URL to for concert notifications. We disregard error responses for brevity.


openapi: 3.1.0
info:
title: SpeakeasyClub
version: 1.0.0
paths:
/registerForAlert:
post:
summary: Register for concert alerts
description: Register a URL to be called when new concerts are scheduled.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
format: uri
description: The URL to be notified about approaching concerts.
example:
url: "http://example.com/notify"
responses:
'200':
description: Registration successful.
callbacks:
concertAlert:
'{$request.body#/url}':
post:
summary: Concert alert
description: Notify the registered URL with details of an upcoming concert.
requestBody:
required: true
content:
text/plain:
schema:
type: string
example: "The Swing Machine will be playing at 19h30 tomorrow. $10 cover charge."
responses:
'200':
description: Notification received by the external service.

Add a Callback to the Path

Finally, add a callback to the endpoint. The callback gets the URL using {$request.body#/url} from the POST to send a string describing the next concert.

Selecting parameters in a POST request in this way is called a runtime expression (opens in a new tab). Read more about the syntax in the specification.


openapi: 3.1.0
info:
title: SpeakeasyClub
version: 1.0.0
paths:
/registerForAlert:
post:
summary: Register for concert alerts
description: Register a URL to be called when new concerts are scheduled.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
format: uri
description: The URL to be notified about approaching concerts.
example:
url: "http://example.com/notify"
responses:
'200':
description: Registration successful.
callbacks:
concertAlert:
'{$request.body#/url}':
post:
summary: Concert alert
description: Notify the registered URL with details of an upcoming concert.
requestBody:
required: true
content:
text/plain:
schema:
type: string
example: "The Swing Machine will be playing at 19h30 tomorrow. $10 cover charge."
responses:
'200':
description: Notification received by the external service.

Add a Subscription Path

Now add a single /registerForAlert endpoint that a user can pass a URL to for concert notifications. We disregard error responses for brevity.

Add a Callback to the Path

Finally, add a callback to the endpoint. The callback gets the URL using {$request.body#/url} from the POST to send a string describing the next concert.

Selecting parameters in a POST request in this way is called a runtime expression (opens in a new tab). Read more about the syntax in the specification.


openapi: 3.1.0
info:
title: SpeakeasyClub
version: 1.0.0
paths:
/registerForAlert:
post:
summary: Register for concert alerts
description: Register a URL to be called when new concerts are scheduled.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
url:
type: string
format: uri
description: The URL to be notified about approaching concerts.
example:
url: "http://example.com/notify"
responses:
'200':
description: Registration successful.
callbacks:
concertAlert:
'{$request.body#/url}':
post:
summary: Concert alert
description: Notify the registered URL with details of an upcoming concert.
requestBody:
required: true
content:
text/plain:
schema:
type: string
example: "The Swing Machine will be playing at 19h30 tomorrow. $10 cover charge."
responses:
'200':
description: Notification received by the external service.

Syntax Rules

The webhooks element has identical syntax to the paths element. Both are lists of Path Item Objects (opens in a new tab). (This makes sense if you consider that a webhook is like a reverse path: Just as paths describe endpoints on the server's API, webhooks describe endpoints on the user's API.)

A callback is also a Path Item Object.

This means a webhook or callback has all the following path properties available to it: $ref, summary, description, get, put, post, delete, options, head, patch, trace, servers, and parameters.

Callbacks and Webhooks in Speakeasy

Speakeasy will automatically include your webhook types (opens in a new tab) in generated code and documentation. You don't need to do anything extra or include Speakeasy extensions (x-speakeasy) to use these features.

Tips

Here are a few more considerations when designing your API's use of webhooks and callbacks:

  • If using callbacks, you can manage multiple subscriptions in the same endpoint using multiple parameters — one for each URL. This might be neater than creating one registration path per callback.
  • If using webhooks, be sure to explain in the summary or description exactly how users can subscribe and unsubscribe, as well as any associated fees for the service.
  • Any description elements may use CommonMark syntax (opens in a new tab).
  • Callbacks and webhooks must have unique names in the schema. The names are case-sensitive.
  • Make your webhooks idempotent. Sending or receiving the same event multiple times should not cause side effects.
  • Protect your API from DDoS attacks by making sure webhooks are protected from spam registrations with authentication and that limits are in place for the rate and size of your messages.

What About AsyncAPI Standard?

OpenAPI is a general-purpose API specification that can be used for asynchronous APIs, but it is not necessarily optimized for them. If you find that OpenAPI is insufficient for your use case, you should check out AsyncAPI (opens in a new tab). Just be aware that AsyncAPI is still in the early stages of development and is not yet widely supported by the tooling ecosystem.

CTA background illustrations

Speakeasy Changelog

Subscribe to stay up-to-date on Speakeasy news and feature releases.