---
title: Receiving and verifying a webhook
description: "For developers: the request, its headers, the JSON message, HMAC-SHA256 signature verification, the expected response and duplicates."
url: "https://illizeo.com/en/centre-aide/webhooks-receiving-and-verifying/"
updated: "2026-11-05"
category: Platform / Webhooks
---

# Receiving and verifying a webhook

This article is for developers who receive Illizeo webhooks: the shape of the request, its headers, the content of the message, signature verification, the expected response and how to handle duplicates. The subscription itself is created in the application (see [Creating and managing a webhook](https://illizeo.com/en/centre-aide/webhooks-creating-and-managing/)).

## Before you start

- Expose a public `https://` address. http addresses, and those leading to an internal network, are refused when saving and at every delivery.
- Get the webhook's signing secret from the administrator. It starts with `whsec_` and is shown only once, on creation or rotation: keep it like a password.

## The request

Each delivery is a `POST` request whose body is a JSON object (`Content-Type: application/json`). It carries these headers:

| Header | Content |
| --- | --- |
| `Illizeo-Event` | The event code, for example `absence.approved`. |
| `Illizeo-Event-Id` | The event identifier, the same for every attempt and for every webhook listening to it. |
| `Illizeo-Delivery` | The identifier of the delivery to this webhook, the same from one attempt to the next. |
| `Illizeo-Signature` | The signature, in the form `t=<unix timestamp>,v1=<hexadecimal signature>`. |
| `User-Agent` | `Illizeo-Webhooks/1.0` |

## The message

The body always contains five fields:

- `id`: the event identifier (repeated in `Illizeo-Event-Id`);
- `event`: the event code;
- `occurred_at`: the date and time of the fact, in ISO 8601 format;
- `tenant`: the company account (its subdomain);
- `data`: the identifiers and states specific to the event.

Example: `{"id":"…","event":"absence.approved","occurred_at":"…","tenant":"…","data":{"absence_id":…,"employee_id":…,"absence_type_id":…,"from":"…","to":"…","status":"…"}}`

The message carries identifiers, dates, states and, for some events, quantities (hours, for example). It never contains a person's name, a social security number, a salary amount or an attachment. For details, read the resource back through the API with a token (see [Public API: developer guide](https://illizeo.com/en/centre-aide/api-developer-guide/)). The `data` fields of each event are listed in the [Webhook event catalogue](https://illizeo.com/en/centre-aide/webhooks-event-catalogue/).

![The JSON message of a test delivery expanded in the deliveries table: id, data (here the test message and endpoint_id), event (ping), tenant and occurred_at fields](https://i0.wp.com/illizeo.com/wp-content/uploads/2026/11/webhooks-receiving-and-verifying-01.en_.png?resize=800%2C118&quality=80&ssl=1)
The message sent, as your system receives it.

## Verifying the signature

1. Read the **raw** request body, before any JSON decoding: the signature covers the bytes received.
2. Split the `Illizeo-Signature` header: the value after `t=` is the timestamp, the one after `v1=` the signature.
3. Compute an HMAC-SHA256 with the webhook secret as key, over the string made of the timestamp, a dot and the raw body: `<t>.<raw body>`. Express the result in hexadecimal.
4. Compare it with `v1` using a constant-time comparison (for example `crypto.timingSafeEqual` in Node.js, `hash_equals` in PHP, `hmac.compare_digest` in Python).
5. Reject the message if the timestamp is more than 5 minutes away from your clock: this blocks the replay of an intercepted message.

The window that shows the secret gives a Node.js example, under **Verifying a delivery**. Each attempt is signed when it is sent, with the secret in force: after a secret rotation, every delivery, retries included, is signed with the new one. There is no period during which both secrets are valid: coordinate the rotation with the administrator.

![The header of the API documentation: introduction, including the Webhooks paragraph describing the Illizeo-Signature header, the HMAC-SHA256 calculation, the 5-minute tolerance, the 10-second response time and the retries, then the base URL](https://i0.wp.com/illizeo.com/wp-content/uploads/2026/11/webhooks-receiving-and-verifying-02.en_.png?resize=800%2C524&quality=80&ssl=1)
The API documentation recalls the signature format and the retries.

## Responding

- Answer with a 2xx code within 10 seconds. Any other code, no answer, or a redirect (which is not followed) counts as a failure.
- Answer first, process afterwards: queue the message on your side, then read the resource back through the API if needed.
- After a failure, Illizeo retries the delivery 1 minute, 5 minutes, 30 minutes, 2 hours and then 12 hours later, six attempts in all. A webhook that fails continuously for three days is deactivated.

## Duplicates and order of arrival

- The same event may arrive more than once, for example if your response took longer than 10 seconds while the message was processed. Keep the `Illizeo-Event-Id` values already processed and ignore a duplicate.
- Retries can make an event arrive after a more recent one. Rely on `occurred_at`, or read the current state of the resource through the API.

## The test message

The **Send a test** button on the webhook page sends a `ping` event, signed like the others, whose `data` contains `endpoint_id` and a short message. Answer it with a 2xx code like any other message. A test is never retried.

## Good to know

- The API documentation, in the application, lists every event and the fields of its `data`, in its **Webhook events** card.
- The event families of a module (Scheduling, Annualised working time, Budget, BDESE, Swiss payroll, Flex Office) are only emitted if the module is enabled in the space.

### *Similaire*
