# openapi-tips-webhooks-callbacks
Source: https://speakeasy.com/blog/openapi-tips-webhooks-callbacks


import { Callout, Table } from "@/mdx/components";

<Callout title="Announcing: OpenAPI Reference" type="default">
Hi! These blog posts have been popular, so we've built an entire [OpenAPI Reference Guide](/openapi/) to answer any question you have.

It includes detailed information on [**webhooks**](/openapi/webhooks) **&** [**callbacks**](/openapi/webhooks/callbacks).

Happy Spec Writing!

</Callout>

## 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](https://spec.openapis.org/oas/v3.0.0#callback-object) in 2017; webhooks in [version 3.1](https://spec.openapis.org/oas/v3.1.0#oasWebhooks) in 2021.

<Table
  title="OpenAPI Version History"
  columns={[
    { key: "version", header: "OpenAPI Version" },
    { key: "date", header: "Date" },
    { key: "notes", header: "Notes" },
  ]}
  data={[
    {
      version: "[3.0.0](https://spec.openapis.org/oas/v3.0.0)",
      date: "2017-07",
      notes: "Callbacks added",
    },
    {
      version: "[3.0.1](https://spec.openapis.org/oas/v3.0.1)",
      date: "2017-12",
      notes: "",
    },
    {
      version: "[3.0.2](https://spec.openapis.org/oas/v3.0.2)",
      date: "2018-10",
      notes: "",
    },
    {
      version: "[3.0.3](https://spec.openapis.org/oas/v3.0.3)",
      date: "2020-02",
      notes: "",
    },
    {
      version: "[3.1.0](https://spec.openapis.org/oas/v3.1.0)",
      date: "2021-02",
      notes: "Webhooks added",
    },
  ]}
  rowKeyField="version"
/>

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.

```yaml filename="openapi.yaml"
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.

```yaml filename="openapi.yaml"
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.
```

### 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.

```yaml filename="openapi.yaml"
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](https://spec.openapis.org/oas/v3.1.0#runtime-expressions). Read more about the syntax in the specification.

```yaml filename="openapi.yaml"
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](https://spec.openapis.org/oas/v3.1.0#pathItemObject). (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](/docs/customize-sdks/webhooks) 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](https://spec.openapis.org/oas/v3.1.0#rich-text-formatting).
- 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](https://www.asyncapi.com/). Just be aware that AsyncAPI is still in the early stages of development and is not yet widely supported by the tooling ecosystem.