Webhooks

Webhooks allow you to push information from Administrate's TMS to an external system, based on triggering events such as record creation and updates. This is useful for integrations that require near-real-time notifications and avoids you from having to periodically poll for updates using our APIs.

For a deep dive into how to discover which events are enabled for Webhooks, and how to create a Webhook and filtered Webhooks see our Core Webhooks documentation

Authentication

Webhooks are encrypted (delivered via TLS) to an endpoint specified during creation.

We currently recommend Basic Auth for authentication, but we also support shared secret authentication by providing a secret in the Webhook's config during setup.

When we send a payload to the endpoint, we include the config so you can validate that the request is coming from Administrate.

OAuth authentication is not well suited for one-way requests like Webhooks. For example, obtaining refresh tokens is not straightforward and introduces additional complexity and overhead when Basic Auth or a shared secret strategy works well.

For more information about setting up Webhooks, see our Core Webhooks documentation on how to set up a Webhook with an example of Basic Auth and shared secret authentication.

HMAC Verification

Our webhook payloads are signed via HMAC (Hash-based message authentication code). These signatures can be used by a consumer to verify the request is genuine and came from Administrate. In order to do this, we will provide you with the secret key stored on our end.

All webhooks sent by our system include:

  • header X-Administrate-signature containing the HMAC hash in hexadecimal format
  • header X-Administrate-signature-timestamp containing the timestamp the request was sent (in seconds from the Unix Epoch)
  • field metadata.sent_at in the request body: the UTC timestamp the request was sent (in ISO 8601 format). This can be used to prevent replay attacks

The below examples show how a webhook request's HMAC signature can be verified on the receiving end.

Example (Python/Flask)

import hmac, os
from flask import Flask, request, jsonify

app = Flask(__name__)

SECRET = os.getenv("HMAC_SECRET")  # secret should be stored securely


@app.route("/test/python", methods=["POST"])
def test_route():
    received_digest = request.headers.get("X-Administrate-signature")
    hmac_object = hmac.new(bytes.fromhex(SECRET), request.data, "SHA512")
    generated_digest = hmac_object.hexdigest()
    if not hmac.compare_digest(received_digest, generated_digest):
        # Verification failed: abort
        return jsonify({"error": "Signature verification failed"}), 401

    # Verification succeeded: process request

    return jsonify({"success": True}), 200


if __name__ == "__main__":
    app.run()

Example (NodeJS/Express)

const { createHmac } = require("crypto");
const express = require("express");

const SECRET = process.env.HMAC_SECRET; // secret should be stored securely
const app = express();
app.use(express.text({ type: "*/*" }));

app.post("/test/nodejs", (req, res) => {
  const receivedSignature =
    req.headers["X-Administrate-signature".toLowerCase()];
  const generatedSignature = createHmac("sha512", Buffer.from(SECRET, "hex"))
    .update(req.body)
    .digest("hex");
  if (generatedSignature !== receivedSignature) {
    // Verification failed: abort
    res.status(401);
    return res.send({ error: "Signature verification failed" });
  }

  // Verification succeeded: process request

  res.status(200);
  return res.send({ success: true });
});

app.listen(8000, () => {});

Best Practices

Prepare for Scale

Webhooks are sent when updates are performed in the Administrate platform. This can cause the volume of Webhooks sent to fluctuate during times of the day.

If a large number of updates are performed then prepare for a large number of Webhooks to be sent to your external system.

Check timestamps

A series of entity updates during a short time interval will result in the same Webhook firing multiple times. We do not guarantee that your external system will receive these Webhooks in the exact same order of those updates that were performed. The triggered_at timestamp in the payload's metadata indicates when the event was triggered:

{
    "metadata": {
        "triggered_at": "2023-07-19T09:05:13.000000Z",
        "webhook_id": "T3V0Ym91bmRIb29rOjg="
    },
    "payload": {}
}

Your system should ignore any webhooks with a timestamp that is earlier than those already processed.

Have separate apps for handling Webhooks

We recommend having a separate integration for handling Webhooks requests.

For example, if you are planning on using an Event Created Webhook to update an external website, do not have an endpoint on the website. This can prevent our Webhook system from potentially overwhelming the website.

Don't process Webhooks during requests

Our Webhooks system will send the generated payload to an endpoint on your external system. You should store the payload and perform any further action in another process, not during the HTTP request.

This will allow you to handle a large number of requests by taking a lot less time to process the Webhook and be more tolerant of errors if your external app is connecting to another system and it is having issues.