OpenAPI callbacks and webhooks¶
Route-level Callbacks need no special handling: a callbacks=[...] argument on a route
is carried over to every versioned copy of it automatically:
callback_router = APIRouter()
@callback_router.post("{$url}")
def on_event(body: dict) -> None: ...
@router.post("/items", callbacks=callback_router.routes)
@api_version((1, 0))
def create_item() -> dict: ...
Webhooks (app.webhooks) are visible in every version's schema by default. Pass
webhook_routers to version them the same way as regular routes, with @api_version on
each definition:
webhook_router = APIRouter()
@webhook_router.post("/order-created")
@api_version((1, 0))
def webhook_order_v1(body: OrderV1) -> None: ...
# Same path and method as v1: replaces it instead of adding a second entry
@webhook_router.post("/order-created")
@api_version((2, 0))
def webhook_order_v2(body: OrderV2) -> None: ...
@webhook_router.post("/payment-failed")
@api_version((1, 0), remove_in=(2, 0))
def webhook_payment_v1(body: dict) -> None: ...
RouterVersioner(
app=app,
routers=router,
webhook_routers=webhook_router,
version_format=VersionFormat.SEMVER,
).versionize()
In webhook_versioning_app.py, which follows this pattern, v1.0 lists two webhooks and v2.0 only order-created, with its v2 definition (the example's second webhook is payment-received rather than payment-failed):


A webhook version only becomes visible once a route version reaches that same prefix, since
both follow the same remove_in lifecycle.