Consuming Webhooks

This guide will go into more detail about the nature of Upmind webhooks, the payload structure, and best practices for configuring your endpoint to consume them.

Quick-Start

The quickest way to start consuming webhooks using your endpoint is to install one of our open source Webhook Endpoint libraries, such as upmind/webhook-endpoint for PHP. Alternatively, the information in the rest of this guide will be sufficient for you to implement the endpoint server code yourself.

Events

Each webhook event corresponds to a hook event (sometimes referred to as a trigger) which has fired within Upmind. This can be as a direct result of user interaction; for example staff logging in or a client resetting their password, or an action performed by the system; such as an automatic renewal invoice payment. In your webhook processing code you'll receive both a hook code (a string used to programatically identify the event e.g invoice_paid_hook) and a unique webhook event id (which can be used as an idempotency token to prevent double-processing).

Payload

Upmind ends webhooks to your endpoint as HTTP POST requests, where the webhook payload is sent in the request body as a JSON string. The payload will always contain a version property (default V1) which determines the payload properties/structure. You can see an example of a V1 webhook payload here.

But no matter which version, all webhook payloads will contain the following data:

  • Unique event id
  • Hook Category
  • Hook
  • Date/time
  • Brand
  • Actor
  • Object (polymorphic, according to the hook category)

Authentication

Each webhook endpoint is issued a random secret. This secret can be used for the basis of authenticating incoming webhooks in your endpoint code. Currently the only method of authentication supported is by means of a payload signature, although additional authentication methods will be available soon.

Click on an Endpoint to view or change its secret

Click on an Endpoint to view or change its secret

For added security you should also check the IP address associated with the incoming request and only proceed if it matches one of Upmind's IP Addresses.

Signature Authentication

Each webhook request contains the header X-Webhook-Signature whose contents is a SHA-256 HMAC hash of the request body, calculated using the webhook endpoint secret. Knowing this, you can make the same hash calculation of incoming webhook request bodies in your endpoint code; comparing your computed signature with the one received in the request headers, and only proceeding if the signatures match.

Examples:

<?php

$secret = 'xxxxxxx'; // your webhook endpoint secret
$requestBody = file_get_contents('php://input') // raw request body;

$expected = hash_hmac('sha256', $requestBody, $secret);

if (!hash_equals($expected, $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'])) {
     http_response_code(401);
     exit('Invalid webhook signature');
}
let crypto = require("crypto");

let endpointSecret = "xxxxxxxxxx"; // your webhook endpoint secret
let requestBody = "{...}"; // raw request body

let signature = (hmac = crypto
  .createHmac("sha256", endpointSecret)
  .update(requestBody)
  .digest("hex"));

// compare signature to x-webhook-signature header, and respond with an error if they don't match

Responses

Upmind will log the HTTP status code and an excerpt of the response body it gets back from your endpoint. You can use this to your advantage and ensure that you return an appropriate status code for the result of the webhook request, which allows you to see, debug and retry any webhook events which failed from your Upmind admin area.

For example, if your endpoint received the webhook successfully, return a HTTP 200 response. If authentication failed (such as an invalid signature header) then return a HTTP 401 response. If something unexpected happened, return a HTTP 500 response.

Timeouts

Webhook requests will time out if your endpoint does not finish responding after 30 seconds, leading to errors in your webhook events log. To mitigate the risk of timeouts, it's generally advised to store the webhook event(s) first and then process them asynchronously, for example using a queueing system like Beanstalk, Redis, RabbitMQ or SQS.