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.
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
- Open “Organization” → webhooks.
- Under “Create webhook”, enter the address – it must start with
https://– and optionally a description. - Under “Events”, choose what you want to receive. Choosing nothing means: all.
- 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. |
Structure of a call
Every call is a POST with a JSON body:
{
"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…
tis the timestamp in seconds,v1is HMAC-SHA256 over<t>.<body>with the endpoint’s signing secret, in hexadecimal.
To verify:
- Read
tandv1from the header. - Take the unmodified body – do not parse it as JSON and serialise it again first.
- Compute HMAC-SHA256 over
t, a dot and the body, and compare it withv1in constant time. - 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:
[$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.
Last checked against the product on September 13, 2026. As Markdown
Related articles
-
Managing websites for clients
How agencies run many client websites in one organization, who declares for them and how clients use the WordPress plugin without an account of their own.
-
Versions, “outdated” and PDF
Why every publication is an unchangeable version, what it means when a statement is marked as outdated, and how to publish a new version.
-
Scheduled scans, login areas and click paths
How to set up a recurring scan, have areas behind a login scanned and reach states through click paths.
Still have a question?
Tell us which question is still open. Questions we hear more than once become new help articles.