Skip to content

Webhooks

OpenAPI Webhooks are those cases where you want to tell your API users that your application could/should/would call their own application, for example, sending a request with specific bits of data, usually to notify them of some type of event.

This also means that instead of your users sending requests to your APIs, it is your application sendind requests to their application.

This process is called webhook.

Esmerald webhooks

Esmerald provides a way of declaring these webhooks in the OpenAPI specification. It is very, very similar to the way the Gateway is declared but dedicated to webhooks.

The process usually is that you define in your code, as normal, what is the message that you will send, in other words, the body of the request.

You also define in some way at which moments your app will send those requests or events.

Your users on the other hand, define some way (web dashboard, for instance) the URL where your application should send those requests.

The way the logic how to register the URLs for the webhooks and the code to performs the said actions is entirely up to you.

Documenting Esmerald webhooks with OpenAPI

As mentioned before, the way of doing it is very similar to the way you declare Gateway but for this purpose, webhooks have a special dedicated object or objects to do make it happen, the WebhookGateway.

Also, the webhooks are not hooked into the application routing system, instead, they are placed in the webhooks list.

from esmerald import Esmerald

app = Esmerald(webhooks=[...])

WebhookGateway

As the name indicated, the WebhookGateway is the main object where you declare the hooks for the OpenAPI specification and unlike the Gateway, it does not declare a path (example, /event), instead, it only needs to receive the name of the action.

Like the Gateway, the WebhookGateway also expects a handler but not the same handler as you usually use for the routes, a special webhook handler.

How to import it

You can import them directly:

from esmerald import WebhookGateway

Or you can use the full path.

from esmerald.routing.gateways import WebhookGateway

Parameters

All the parameters and defaults are available in the WebhookGateway Reference.

Handlers

The handlers for the webhooks are pretty much similar to the normal handlers used for routing but dedicated only to the WebhookGateway. The available handlers are:

  • whget - For the GET.
  • whpost - For the POST.
  • whput - For the PUT.
  • whpatch - For the PATCH.
  • whdelete - For the DELETE.
  • whead - For the HEAD.
  • whoptions - For the OPTION.
  • whtrace - For the TRACE.
  • whroute - Used to specificy for which http verbs is available. This handler has the special methods attribute. E,g.:

    from esmerald import whroute
    
    @whroute(methods=["GET", "POST"])
    ...
    

As you can already see, the handlers are very similar to the routing handler but dedicated to this purpose and all of them start with wh.

The wh at the beginning of each handler means WebHook.

How to import them

You can import them directly:

from esmerald import (
    whdelete,
    whead,
    whget,
    whoptions,
    whpatch,
    whpost,
    whput,
    whroute,
    whtrace
)

Or via full path.

from esmerald.routing.webhooks.handlers import (
    whdelete,
    whead,
    whget,
    whoptions,
    whpatch,
    whpost,
    whput,
    whroute,
    whtrace
)

An Esmerald application with webhooks

When you create an Esmerald application, as mentioned before, there is a webhooks attribute that you use to define your application webhooks, in a similar way you define the routes.

from datetime import datetime

from pydantic import BaseModel

from esmerald import Esmerald, Gateway, post
from esmerald.routing.gateways import WebhookGateway
from esmerald.routing.webhooks.handlers import whpost


class Payment(BaseModel):
    is_paid: bool
    amount: float
    paid_at: datetime


@whpost("new-event")
async def new_event(data: Payment) -> None: ...


@post("/create")
async def create_payment(data: Payment) -> None: ...


app = Esmerald(
    routes=[Gateway(handler=create_payment)],
    webhooks=[WebhookGateway(handler=new_event)],
)

Note how the whpost and post are declared inside the webhooks and routes respectively, similar but not the same and how the whpost does not require the / for the path.

The webhooks you define will end up in the OpenAPI schema automatically.

Using the APIView to generate webhooks

Since Esmerald supports class based views, that also means you can also use them to generate webhooks.

from datetime import datetime

from pydantic import BaseModel

from esmerald import APIView, Esmerald, Gateway, post
from esmerald.routing.gateways import WebhookGateway
from esmerald.routing.webhooks.handlers import whpost


class Payment(BaseModel):
    is_paid: bool
    amount: float
    paid_at: datetime


class PaymentWebhook(APIView):
    @whpost("new-event")
    async def new_event(self, data: Payment) -> None: ...

    @whpost("payments")
    async def new_payment(self, data: Payment) -> None: ...


@post("/create")
async def create_payment(data: Payment) -> None: ...


app = Esmerald(
    routes=[Gateway(handler=create_payment)],
    webhooks=[WebhookGateway(handler=PaymentWebhook)],
)

Important

Notice that with webhooks you are actually not declaring a path (like /user). The text you pass there is just a name or an identifier of the webhook (name of the event).

This happens because it is expected that your users would actually define the proper URL path where they want to receive the webhook in some way.

Check out the docs

Let us see how it would look like in the docs if we were declaring the webhooks from the examples.

First example, no Class Based Views

Second example, with Class Based Views