---
title: "Brantial Webhooks: Setup and Signature Guide"
description: "Send signed JSON events to Zapier, Make, n8n or your own endpoint. Get the address, check the signature in PHP, Node.js or Python, and fix common errors."
source_url: "https://brantial.ai/help/webhooks/"
lang: "en"
updated: "2026-10-23T00:00:00.000Z"
---

Help

# Send Brantial events to Zapier, Make, n8n or your own system

Send signed JSON events to Zapier, Make, n8n or your own endpoint. Get the address, check the signature in PHP, Node.js or Python, and fix common errors.

Updated October 23, 2026

A webhook sends your project’s alerts and reports to Zapier, Make, n8n or your own system as JSON, the moment they happen. From there you can post them to any tool, add rows to a spreadsheet or open tickets. One address receives every event, and each request is signed so you can check that it came from Brantial.

## Before you start

- **Only the project owner can connect a webhook.**
- **The address must be public and use HTTPS.** Local, private-network and plain HTTP addresses are refused.
- One project has one webhook address. To send events to several places, fan them out in your workflow.

## Get an address

### Zapier

1. Create a Zap and choose **Webhooks by Zapier** as the trigger.
2. Pick the **Catch Hook** event and continue.
3. Copy the webhook URL Zapier shows. It starts with `https://hooks.zapier.com/`.

### Make

1. Create a scenario and add the **Webhooks → Custom webhook** module.
2. Click **Add**, name the webhook and save.
3. Copy the address Make shows, and turn the scenario on so it keeps listening.

### n8n

1. Add a **Webhook** node and set **HTTP Method** to `POST`.
2. Copy the **Production URL**, not the test URL.
3. Activate the workflow. The production URL only answers while the workflow is active.

### Your own system

Any HTTPS endpoint that accepts a `POST` with a JSON body and answers with a `2xx` status works.

## Connect it in Brantial

1. In Brantial, open your project, go to **Settings** and open the **Notifications** tab.
2. In the **Webhook** card, paste the address into **Webhook URL**.
3. Click **Connect webhook**. Brantial sends a `test` event first and saves the address only if it is accepted.

![The Webhook card in Brantial's notification settings with a Zapier address filled in](https://brantial.ai/images/help/webhooks/en/1-connect.webp)

The address here is an example.

Once connected, the card shows your **Signing secret** and a switch for each event. Turn off the events you do not want; changes save straight away. **Send test** sends another `test` event whenever you need one, for example while you build your workflow.

![The connected Webhook card with the signing secret and a switch for each event name](https://brantial.ai/images/help/webhooks/en/2-connected.webp)

The secret here is an example. Yours is on your card.

## What Brantial sends

Every event has the same shape, so a workflow step can use `title`, `text` and `url` without knowing the event. The figures behind each event are in `data`.

```
{
  "id": "6f1c2d4e-8a90-4b7c-9d2e-1f3a5b7c9d0e",
  "event": "report.summary",
  "created_at": "2026-10-26T07:00:00+00:00",
  "project": {
    "id": 1234,
    "name": "Example Outdoor",
    "domain": "example.com",
    "url": "https://app.brantial.ai/1234/overview"
  },
  "title": "Weekly report for Example Outdoor",
  "text": "Weekly report for Example Outdoor — visibility 42%, up 3 points.",
  "url": "https://app.brantial.ai/1234/overview",
  "data": {
    "cadence": "weekly",
    "period_start": "2026-10-19",
    "period_end": "2026-10-25",
    "metrics": {
      "visibility": { "value": 42, "change": 3 },
      "mentions": { "value": 118, "change": 12 },
      "average_position": { "value": 2.4, "change": -0.3 },
      "sentiment": { "value": 71, "change": 1 }
    },
    "strongest_prompts": [
      { "prompt": "best hiking boots for wide feet", "visibility": 80 }
    ]
  }
}
```

The figures above are an example. Each event’s `data`:

| Event | When | `data` |
| --- | --- | --- |
| `test` | When you connect or click **Send test** | Empty |
| `report.summary` | Every Monday, or monthly if the project is set to that | `cadence`, `period_start`, `period_end`, `metrics`, `strongest_prompts` |
| `site_audit.finished` | When a site audit finishes or crosses your thresholds | `audit_id`, `site_health_score`, `ai_readiness_score` |
| `visibility.changed` | When AI visibility moves 10 points or more in a week | `current`, `previous`, `delta`, `answers` |
| `competitors.new` | When AI keeps mentioning a brand you do not track | `brands` |
| `optimization.finished` | When an optimized page is ready or needs your input | `optimization_id`, `page_url`, `status` |
| `crawler_access.changed` | When an AI crawler is blocked or allowed | `changes`, `unreachable` |

Each request also carries these headers:

- `X-Brantial-Event`: the event name, the same as `event` in the body.
- `X-Brantial-Delivery`: the event’s `id`. A retried event keeps the same id, so you can skip one you have already handled.
- `X-Brantial-Signature`: the signature described below.

## Check the signature

`X-Brantial-Signature` is `sha256=` followed by the HMAC-SHA256 of the raw request body, using your signing secret as the key. Compute the same value on your side and compare. Use the body exactly as it arrived: if your framework parses and re-encodes the JSON first, the signature will not match.

In Zapier, Make and n8n this check is optional, because only Brantial knows the address. On your own system, always check it.

**PHP**

```
$secret = getenv('BRANTIAL_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
$expected = 'sha256='.hash_hmac('sha256', $body, $secret);
$received = $_SERVER['HTTP_X_BRANTIAL_SIGNATURE'] ?? '';

if (! hash_equals($expected, $received)) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);
```

**Node.js**

```
import crypto from 'node:crypto';

export function isFromBrantial(rawBody, signature, secret) {
  const expected =
    'sha256=' +
    crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return (
    typeof signature === 'string' &&
    signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
  );
}
```

With Express, read the body with `express.raw({ type: 'application/json' })` so `req.body` is the raw bytes, and pass `req.get('X-Brantial-Signature')`.

**Python**

```
import hashlib
import hmac

def is_from_brantial(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")
```

With Flask, pass `request.get_data()` and `request.headers.get("X-Brantial-Signature")`.

**Changing the secret.** Click **New secret** on the card. The old secret stops working at once, so update your system straight after.

## Deliveries and retries

- Answer with any `2xx` status within 10 seconds. Brantial does not follow redirects.
- If your address times out, answers with a `5xx` or asks Brantial to slow down with `429`, Brantial tries again: after 1, 5, 15 and 30 minutes, up to five attempts in all.
- If your address answers `404` or `410`, Brantial treats the webhook as removed and stops sending. Connect again to resume.
- Do the slow work after you answer, not before, so the request does not time out.

## Troubleshooting

### “Use a public HTTPS address”

Copy the address again from your tool. It must start with `https://` and must not point to a local or private network.

### “The address did not accept the test event”

The number in brackets is the status your address answered with. The workflow is usually off: turn the Zap, scenario or n8n workflow on, then connect again. In n8n, make sure you copied the **Production URL**.

### “The address answered that it no longer exists”

The hook, scenario or workflow was deleted or turned off. Turn it back on, or create it again and connect with the new address.

### “The webhook no longer accepts events”

After it was connected, your address answered `404` or `410`, so Brantial stopped sending. Fix the workflow and connect again.

### “The last message could not be delivered”

One event did not get through after all its retries. If **Send test** works, the next event will go through as usual.

### My signature check fails

- Check the raw body, not a parsed and re-encoded copy.
- Make sure you use the current secret. After **New secret**, the old one no longer matches.
- Compare the whole header value, including the `sha256=` prefix.

## Disconnect

Click **Disconnect** on the Webhook card. Brantial stops sending events to the address.
