Set up Webhooks
Introduction
Webhooks are a way for your application to provide on-request information to our Manages Services. They are essentially HTTP callbacks that are triggered by specific events. This mechanism allows you to extend the functionality of our Managed Services by integrating them with your services.
A webhook is a URL configured in our service, which can be invoked via an HTTP
POST request. This request is accompanied by a digital signature to
authenticate that it originates from our service. Webhooks operate on a
synchronous request-and-response pattern. Our service initiates this pattern
by sending a JSON message as a POST request, with the expectation of receiving
a JSON response in the application/json
format.
Configure a Webhook
You can configure your webhooks for our Managed Services that support webhooks. One example is the Image Service where you can validate the image metadata to ensure that the image being uploaded conforms with any quality requirements.
service settings
To configure the webhook, click on the desired webhook row in the webhook list. This will navigate you to the Create Webhook station. Provide a URL for the webhook and click on Proceed. This will create a secret for the webhook. Copy the webhook secret and store it securely. This secret is required to validate the authenticity of the request coming to the webhook as described in the next section.
Creating a webhook
When a Managed Service wants to get additional information from your service,
it calls your service via an HTTP POST
request to the URL that you configured
in the settings (e.g. the image upload webhook).
The payload of this request will have metadata related to the specific action
that will allow you to build the required response logic around it.
After your evaluation/action is completed, the webhook must return an HTTP response 200 for a successful action or a 401 status code containing errors if there are any.
To check the authenticity of the request and to ensure that the request has not been tampered with, webhook requests are digitally signed.
Request
Format
Webhook messages have a JSON request body with the following properties:
Field Name | Example | Description |
---|---|---|
payload | { "movie-id": 123 } | Payload containing webhook-specific data |
message_id | 41409546-df67-42b6-b5c4-c006b26b6bad | Unique identifier for the message |
timestamp | 2022-09-22T09:26:10.498Z | Timestamp indicating when the message was sent |
message_type | PublishEntityXYZ | Type of the message |
message_version | 1.0 | Version of the message format, e.g., "1.0" |
{
"payload":{
"movie-id": 123
},
"message_id":"41409546-df67-42b6-b5c4-c006b26b6bad",
"timestamp": "2022-09-22T09:26:10.498Z",
"message_type": "PublishEntityTypeA",
"message_version": "1.0"
}
The Webhook JSON schema payloads and Typescript interface for the messages can be found in the @axinom/mosaic-messages package.
Validating the webhook request
The secret created during the configuration of the webhook is used to create a
hash-based message authentication code
(HMAC)
with SHA-256. This HMAC is created by using the stringified value of the
request body and is attached to the request headers through the custom header
x-mosaic-signature
.
The webhook can use the same secret to validate if the webhook data has been
tampered with using the received request body. For this validation, the
webhook implementor can use the validateWebhookRequest
method in the
@axinom/mosaic-service-common library.
You can alternatively verify the signature manually. Take the full request body in its raw form and append the webhook secret. Then you compute the HMAC with the SHA256 hash function from this string. If the calculated one matches the one from the X-Mosaic-Signature the request is valid and should be processed.
Response
Your webhook response must have a JSON body with the following properties:
Field Name |
Example |
Description |
---|---|---|
|
|
Payload containing webhook-specific data |
|
|
An array of error objects with message and code |
|
|
An array of warning objects with message and code |
|
|
Version of the message format |
{
"payload":{
"valid": "true"
},
"errors": [{
"message": "The error message",
"code": "ERROR_CODE_1"
}],
"warnings": [{
"message": "The warning message",
"code": "WARNING_CODE_1"
}],
"message_version": "1.0"
}
The Webhook JSON schema responses and Typescript interface for the responses can be found in the @axinom/mosaic-messages package.
Example webhook implementation
This code provides an HTTP POST endpoint under the /demo
URL path. The
verifyWebhookRequestMiddleware
is used to verify that the request is
correctly signed. When everything is fine, it returns a payload that contains
demo: true
.
import {
Dict,
generateWebhookResponse,
handleWebhookErrorMiddleware,
verifyWebhookRequestMiddleware,
WebhookRequestMessage,
} from '@axinom/mosaic-service-common';
import { Express, NextFunction, Request, Response } from 'express';
import { Config } from '../../common';
export const setupDemoWebhookEndpoint = (
app: Express,
config: Config,
): void => {
app.post(
'/demo',
// Mosaic middleware to validate Request signature
verifyWebhookRequestMiddleware({
webhookSecret: config.demoWebhookSecret,
payloadJsonSchema: DemoWebhookRequestPayloadSchema,
}),
// Mosaic middleware to return a 401 response in case of an error
handleWebhookErrorMiddleware,
// Your actual business logic implementation
async (
req: Request<
Dict<string>,
Dict<string>,
WebhookRequestMessage<DemoWebhookRequestPayload>
>,
res: Response,
next: NextFunction,
) => {
try {
const response = generateWebhookResponse<DemoPayload>({
payload: {
demo: true,
},
});
res.status(200).send(response);
} catch (error) {
handleWebhookErrorMiddleware(error, req, res, next);
}
},
);
};
Using webhooks during development
As stated before, you need to configure a corresponding webhook URL for a service in the Administration Portal. This is straightforward when the service containing the webhook endpoint is already deployed and has a publicly accessible URL.
But during the development, the Managed Services are not able to just make
requests to your running localhost services (e.g.
http://localhost:11600/manifest
), since they are not publicly accessible. To make
this possible and to be able to test that your webhook actually works as
intended in a more convenient way during the development, it is possible to use
one of the many tunneling
solutions to expose your localhost endpoint as a public URL, allowing managed
services to call your development service instance.