---
title: "Integrating Webhooks"
url: "https://adaptocms.com/docs/integrating-webhooks/"
source: adaptocms.com
---

# Integrating Webhooks

Adapto CMS webhooks send a signed HTTP POST to a URL you control whenever triggered from the backoffice. Use them to notify external systems — revalidation endpoints, Slack bots, build triggers, data pipelines — on demand.

* * *

## Configuration

In the backoffice, go to **Developer Tools → Webhooks** and create a new webhook with:

-   **Label** — an internal name for identifying the webhook
-   **URL** — the HTTPS endpoint that will receive the POST

A signing secret is generated automatically on creation. It is shown once — copy and store it securely. The backoffice displays only a truncated version thereafter, with a copy button for the full value.

* * *

## Delivery

Every delivery is an HTTP POST with `Content-Type: application/json` and the following body:

```
{
  "event_type": "manual",
  "tenant_id": "your-project-uuid",
  "timestamp": "2024-01-15T12:00:00",
  "data": {
    "environment": "production",
    "triggered_by": "deployment-script"
  }
}
```

`data` contains the key-value pairs entered in the trigger dialog.

* * *

## Signature verification

Each delivery includes an `adapto-signature` header containing an HMAC-SHA256 signature of the raw request body, formatted as `sha256={hex_digest}`. Verify it on your receiving server before processing the payload:

```
import crypto from 'crypto';

function verifyAdaptoSignature(
  rawBody: string,
  secret: string,
  signatureHeader: string,
): boolean {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader),
    Buffer.from(expected),
  );
}

// Express example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['adapto-signature'] as string;
  if (!verifyAdaptoSignature(req.body.toString(), process.env.WEBHOOK_SECRET!, signature)) {
    return res.status(401).send('Invalid signature');
  }
  const payload = JSON.parse(req.body.toString());
  // handle payload.data ...
  res.sendStatus(200);
});
```

Use `express.raw()` (or your framework's equivalent) to access the raw bytes before any JSON parsing — the HMAC is computed over the exact bytes sent, so parsing and re-serializing first will produce a different digest.

* * *

## Triggering

From the webhook list, use the **Trigger** button to fire a delivery immediately. The trigger dialog accepts an optional payload of typed key-value pairs (string, number, or boolean) that populate the `data` field in the request body.
