# Setting up webhooks and verifying signatures

How your system learns when a scan has finished or a statement is outdated – with events, payload, signature verification and retries.

**In short**

- A webhook calls your HTTPS address as soon as a chosen event occurs – for all websites of the organization.
- Every call is signed with HMAC-SHA256; verify signature and timestamp before acting on it.
- Respond with a 2xx status code. Failed deliveries are retried; after persistent errors the endpoint is switched off.

A webhook saves your system from polling. It belongs to the organization, not to a single website: one endpoint fires for all websites. “Owner” and “Administration” may set it up.

## Setting it up

1. Open “Organization” → webhooks.
2. Under “Create webhook”, enter the address – it must start with `https://` – and optionally a description.
3. Under “Events”, choose what you want to receive. Choosing nothing means: all.
4. Use “Test event” to check that your endpoint is reachable and verifies the signature correctly.

The page shows the signing secret via “Show signing secret”. “Generate a new secret” replaces it – only the new one is valid afterwards.

## Events

| Event | When |
| --- | --- |
| `scan.completed` | A scan has finished. |
| `scan.failed` | A scan has failed. |
| `declaration.published` | A new version of a statement has been published. |
| `declaration.outdated` | A published statement has been marked as outdated: [Versions, “outdated” and PDF](https://barrierepruefung.de/en/help/versions#veraltet). |

## Structure of a call

Every call is a `POST` with a JSON body:

```json
{
  "id": "01J…",
  "type": "scan.completed",
  "created_at": "2026-09-13T08:14:00+00:00",
  "data": { }
}
```

`data` contains the details of the event. `id` is the same for all endpoints of the same delivery. Process each `id` only once – a retry can bring the same message a second time.

## Verifying the signature

The `A11y-Signature` header looks like this:

```
A11y-Signature: t=1789286400,v1=5257a869e7…
```

- `t` is the timestamp in seconds,
- `v1` is HMAC-SHA256 over `<t>.<body>` with the endpoint’s signing secret, in hexadecimal.

To verify:

1. Read `t` and `v1` from the header.
2. Take the **unmodified** body – do not parse it as JSON and serialise it again first.
3. Compute HMAC-SHA256 over `t`, a dot and the body, and compare it with `v1` in constant time.
4. Reject calls whose timestamp is older than 5 minutes. The timestamp is part of the signature – so a captured call cannot be replayed later.

Example in PHP:

```php
[$t, $v1] = [null, null];
foreach (explode(',', $_SERVER['HTTP_A11Y_SIGNATURE'] ?? '') as $part) {
    [$key, $value] = array_pad(explode('=', trim($part), 2), 2, null);
    if ($key === 't') { $t = (int) $value; }
    if ($key === 'v1') { $v1 = $value; }
}

$body = file_get_contents('php://input');
$expected = hash_hmac('sha256', $t.'.'.$body, $secret);

$valid = $t !== null && $v1 !== null
    && hash_equals($expected, $v1)
    && abs(time() - $t) <= 300;
```

## Delivery, retries, switching off

- **Successful** is a delivery your endpoint answers within 15 seconds with a 2xx status code. Respond quickly and process afterwards.
- **Retried** are timeouts, server errors and the status codes 408 and 429 – several times, with growing intervals of up to two hours.
- **Not retried** are other 4xx status codes: the problem is in the request or configuration, and a retry would change nothing.
- **Switched off** is an endpoint after repeated failures in a row, and also when its address points to an internal network. The page shows the last error; after fixing it, switch the endpoint on again.

“Show recent deliveries” lists each delivery’s time, event and result. You can pause an endpoint temporarily.

## Tokens of the organization

The same page lists all API tokens of your organization. A token is created on the respective website: [Setting up and connecting the WordPress plugin](https://barrierepruefung.de/en/help/wordpress-setup#token).

---

Last checked against the product: 2026-09-13 · https://barrierepruefung.de/en/help/webhooks
