Webhook and Callback Extensions
Group webhooks and callbacks, route incoming events by a discriminator, and describe how to verify their signatures with x-webhooks and x-callbacks.
OpenAPI 3.x describes the requests your API sends to clients as webhooks (the root webhooks object) and callbacks (an operation's callbacks). The x-webhooks and x-callbacks extensions, at the root of your definition, add what a client needs to handle these requests:
- Groups of related webhooks or callbacks.
- A discriminator that tells which webhook or callback an incoming request is.
- Signature verification that tells how to check that a request came from your API.
x-webhooks and x-callbacks work the same way. Each takes one of two shapes: a single group written directly under the extension, or several named groups. You can't mix the two shapes in one extension.
For which SDK languages support webhooks and callbacks, see Languages & Features.
Use a single group
When all your webhooks share the same discriminator and signature, put description, discriminator, and signatureVerification directly under the extension. APIMatic imports them as one group named default, and every webhook or callback without a group belongs to it.
x-webhooks:
description: Payment status events
discriminator:
propertyPointer: $request.body#/event_type
mapping:
payment.created: paymentCreated
payment.updated: paymentUpdated
signatureVerification:
signatureHeader: X-Webhook-Signature
timestampHeader: X-Webhook-Timestamp
replayTolerance: 300
signatureTemplate: "t={$request.header.X-Webhook-Timestamp},sha256=hex(hmacSha256({$request.header.X-Webhook-Timestamp}.{$request.body}))"
webhooks:
paymentCreated:
post:
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/PaymentEvent"
paymentUpdated:
post:
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/PaymentEvent"| Property | Type | Description |
|---|---|---|
description | string | Description of the group |
discriminator | Discriminator | How to tell the webhooks or callbacks apart |
signatureVerification | Signature verification | How to verify the signature of a request |
In this shape, the discriminator's propertyPointer can point to a top-level property of the request body, such as $request.body#/event_type, or to a request header, such as $request.header.X-Event-Type.
Signature verification
| Property | Type | Description |
|---|---|---|
signatureHeader | string | Required. Request header that carries the signature, for example X-Webhook-Signature |
signatureTemplate | string | Required. Template of the full value of the signature header. See below. |
timestampHeader | string | Request header that carries the time the request was sent, in Unix seconds. Set it together with replayTolerance. |
replayTolerance | integer | Allowed clock difference, in seconds, for replay protection. A positive number, set together with timestampHeader. |
description | string | Description of the verification |
The signatureTemplate contains at least one digest block of the form encoding(algorithm(message)):
encodingishex,base64, orbase64url.algorithmishmacSha256orhmacSha512.messageis the signed content. It can't be empty.
The message and the text around the digest block can contain the placeholders {$request.body} (the raw request body) and {$request.header.Name} (the value of request header Name). Placeholders inside the digest block are signed; text and placeholders outside it are only part of the header's format.
| Template | Header value |
|---|---|
sha512=base64(hmacSha512({$request.body})) | sha512= followed by the Base64 HMAC-SHA512 of the body |
t={$request.header.X-Timestamp},v1=hex(hmacSha256({$request.header.X-Timestamp}.{$request.body})) | t= and the timestamp, then ,v1= and the hex HMAC-SHA256 of the timestamp, a period, and the body |
Use named groups
When your webhooks or callbacks need different discriminators or signatures, define several groups. Each key under the extension is a group name.
x-webhooks:
rides:
description: Ride lifecycle events
discriminator:
propertyPointer: $request.body#/event_type
mapping:
ride.status.updated: rideStatusUpdated
driver.assigned: driverAssigned
payloadVerification:
signatureHeader: X-Signature
algorithm: HMAC-SHA256
digestEncoding: hex
messageTemplate: "{$request.body}"
signatureValueTemplate: "sha256={digest}"| Property | Type | Description |
|---|---|---|
description | string | Description of the group |
discriminator | Discriminator | How to tell the group's webhooks or callbacks apart. The propertyPointer must point into the request body. |
payloadVerification | Payload verification | How to verify the signature of a request. See below. |
Because description, discriminator, signatureVerification, and payloadVerification mark the single-group shape, you can't use them as group names. A named group uses payloadVerification, not signatureVerification.
Payload verification
| Property | Type | Description |
|---|---|---|
signatureHeader | string | Required. Request header that carries the signature |
algorithm | HMAC-SHA256 or HMAC-SHA512 | Required. Signing algorithm |
digestEncoding | hex, base64, or base64url | Encoding of the digest |
messageTemplate | string | Template of the signed message. See below. |
signatureValueTemplate | string | Template of the signature header's value. Must contain {digest}, for example sha256={digest}. |
description | string | Description of the verification |
messageTemplate can contain these placeholders, separated by exactly one character, such as a period:
| Placeholder | Value |
|---|---|
{$request.body} | Raw request body |
{$method} | HTTP method |
{$request.header.Name} | Value of request header Name |
{$request.body#/path} | Value at a JSON pointer in the request body |
Assign webhooks and callbacks to a group
With named groups, assign each webhook or callback to its group by setting x-webhook-group or x-callback-group in its Operation Object. The value must be the name of a group in x-webhooks or x-callbacks.
webhooks:
rideStatusUpdated:
post:
x-webhook-group: rides
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/RideStatusEvent"
paths:
/rides:
post:
callbacks:
rideStatusUpdatedCallback:
"{$request.body#/callback_url}":
post:
x-callback-group: rideUpdates
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/RideStatusEvent"With a single group, you don't need these extensions: webhooks and callbacks without one belong to the default group.
Discriminator
The discriminator tells which webhook or callback an incoming request is.
| Property | Type | Description |
|---|---|---|
propertyPointer | string | Where the event type is in the request, for example $request.body#/event_type |
mapping | map of strings | Maps each event type value to the name of a webhook (a key of webhooks) or callback (a key of an operation's callbacks) |
Validation
The rules for these extensions are in the OpenAPI Webhooks, OpenAPI Callbacks, and OpenAPI Webhook and Callback Signature Verification sections of OpenAPI v3 APIMatic Linting, and in the Webhooks section of APIMatic CodeGen Validation.