Monitoring System
How It Works
Alerts sent via email or webhooks are notifications dispatched according to a chosen frequency. Synchronizations must be configured through the data type sync settings to enable the completion of the events defined in the alert.
The synchronization frequency must align with the alert frequency to ensure proper functionality.
Learn more about sync settings here.
When an event is configured and triggered, a summary email is sent based on the email frequency. The email includes a section for each user.
Once alerts are defined based on the selected events and sync settings are configured accordingly, users can be assigned to alerts.
Alerts can be restricted with filters on the fields of the monitored data type — for example, only legal notices whose source is Observations RNE, or only collective procedures where an item of the administrators array matches a given type, name or city. Filters on sub-fields of array fields are supported. Ask a Qard staff member to configure them on your alert.
Key Steps:
Alert creation must be coordinated with a Qard staff member.
A list of configured alerts can be retrieved using the endpoint:
GET/api/v6/clients-notificationsExample response:
{
"notifications": [
{
"name": "Mouvements de dirigeants",
"events": ["ADD", "DELETE"],
"data_types": ["COMPANY_OFFICER"],
"emails": ["risk@example.com"],
"frequency": "NOW",
"url": "https://your-app.example.com/hooks",
"status": "ENABLED",
"auto_assign_new_users": true,
"created_at": "2026-09-17 08:57:00",
"updated_at": null
}
]
}The
nameof an alert is the value to use innotificationsbelow.A user can be assigned to alerts when it is created, with the
notificationsfield ofPOST /api/v6/users/legalorPOST /api/v6/users/natural:POST /api/v6/users/legal{
"siren": "443061841",
"name": "My customer",
"notifications": ["Mouvements de dirigeants"]
}To change the alerts of an existing user, use the following endpoint:
PATCH/api/v6/users/{userId}Example request body:
{
"name": "string",
"group": "default",
"notifications": ["Mouvements de dirigeants"]
}The
notificationsfield replaces the alerts of the user: list every alert the user must stay assigned to. An empty list removes them all; it has to be sent withnameorgroup, since a request with nothing else to change is refused.
An unknown alert name is refused with a 400 on both endpoints, and nothing is changed; so is a
notifications value that is not a list of names ("notifications": "A" instead of ["A"]). An
alert named explicitly in notifications is assigned even when it is DISABLED, on both endpoints:
the user then receives it once the alert is enabled again.
Assigning new users automatically
Ask a Qard staff member to apply an alert automatically to your new users: every user you create
afterwards, with POST /api/v6/users/legal or POST /api/v6/users/natural, is then assigned to it
without any further call, as long as the alert is ENABLED. Such an alert shows
"auto_assign_new_users": true in GET /api/v6/clients-notifications. Users created before are not
assigned retroactively: use the PATCH above for them.
When the creation request carries a notifications field, it takes precedence: the user is assigned
to the alerts listed, and only to them — to none with an empty list []. Leave the field out to get
the automatic alerts.
notifications in POST /users/legal or /users/natural | Alerts assigned to the new user |
|---|---|
left out (or null) | the alerts applied automatically to new users |
["A", "B"] | A and B only |
[] | none |
Webhook Delivery
Every alert whose url is set is delivered as a single POST request.
When a request is sent
Pending events are collected every 5 minutes. On each pass, and for each alert, one request is sent containing everything that has accumulated since the previous send, grouped by user, then by data type. Whether a request is due depends on the alert frequency:
| Frequency | A request is sent |
|---|---|
NOW | on every pass, as soon as at least one event is pending (so at most every 5 minutes) |
DAILY | once a day |
WEEKLY | once a week |
MONTHLY | once a month |
There is no "grouped" and "ungrouped" mode: the body always has the same shape. What varies is how many companies and how many events fell into the window — one company changed since the last send means one entry, ten companies mean ten entries. A very large notification is the only case where more than one request is sent; see Large notifications are split.
Request
POST https://your-app.example.com/hooks?notificationName=<alert id>&date=<unix timestamp>&nonce=<nonce>
User-Agent: Qard API
X-Webhook-Signature: sha256=<base64 hmac_sha256(secret, requestUrl)>
X-Qard-Signature: t=<unix timestamp>,v1=<base64 hmac_sha256(secret, "<t>.<raw body>")>
Two signatures are sent, both computed with your webhook secret:
| Header | Covers | Use |
|---|---|---|
X-Webhook-Signature | the complete URL, query arguments included | historical, same rule as the lifecycle Webhooks |
X-Qard-Signature | the timestamp and the body | prefer this one: it proves the content, not only the address |
Verifying the body signature, on the raw body, before any JSON parsing:
const [stamp, signature] = request.get("X-Qard-Signature").split(",");
const timestamp = stamp.slice(2); // t=1789034891
const received = signature.slice(3); // v1=<base64>
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("base64");
const valid =
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected)) &&
Math.abs(Date.now() / 1000 - Number(timestamp)) < 300; // reject replays
Retries
A delivery that does not answer with a 2xx or 3xx is sent again, with an increasing delay:
1 min, then 5, 10, 15, 30, 45, 60 min, 2 h, 8 h and 8 h. When the last attempt fails the alert is
disabled and has to be reactivated.
Every attempt carries the same notification_id: use it as an idempotency key and ignore a body
you have already processed. Acknowledge quickly with any 2xx and process asynchronously — the delay
of your endpoint is the delay of the delivery.
Large notifications are split
A body larger than 1 MB is delivered as several requests, cut company by company. The parts share one
batch_id and are numbered by part / parts; each one is self-contained, has its own
notification_id and its own retries.
The parts have no delivery order: they may arrive out of order, or at the same time. Process each
one on its own, and regroup on batch_id only if you really need the whole set.
Webhook Structure
An envelope, then a flat list of events. Everything you need sits on the event itself.
{
"notification_id": "9f1c4b0e-1d2a-4c77-8a0e-6b5c2d3e4f50",
"batch_id": "7a20cd91-0f3b-4d18-9f2e-1c7a5b6d8e90",
"part": 1,
"parts": 1,
"sent_at": "2026-09-10T08:50:03+00:00",
"alert": "annonces-legales",
"events": [
{
"kind": "add",
"data_type": "LEGAL_NOTICE",
"user_id": "803eb4fb-2589-4e48-b91f-e6d3b9fdba43",
"siren": "449727205",
"name": "ARIANA PHARMACEUTICALS (449727205)",
"id": "2b4690e8-0b28-4b97-a3d9-ce9fa536b7b9",
"object": { "date": "2026-09-08", "source": "Observations RNE", "category": "Redressement judiciaire", "…": "…" },
"updates": []
}
]
}
Envelope
| Field | Description |
|---|---|
notification_id | Identifies this delivery. Stable across retries — your idempotency key. |
batch_id | Shared by every part of one notification. Equal to a single delivery when parts is 1. |
part / parts | Position of this part and how many there are. 1 / 1 when nothing was split. |
sent_at | When the notification was built, ISO 8601. |
alert | Name of the alert, as returned by GET /api/v6/clients-notifications. |
events
One entry per event, with everything needed on the entry itself.
| Field | Description |
|---|---|
kind | add, update or delete. |
data_type | LEGAL_NOTICE, COLLECTIVE_PROCEDURE, OFFICER… |
user_id | The monitored user, the same id used everywhere in the API. |
siren | SIREN of the company, null for a natural person. |
name | Company name and SIREN, or first and last name. |
id | Id of the object that changed, the one the API serves for that object. |
object | The object itself, in the same format as its data type endpoint. |
updates | The fields that changed, as {"before": …, "after": …} pairs. Always [] on an add or a delete. |
Bodies delivered before September 2026 carried a different shape, with an event name and a users
array holding the changes keyed by data type. It is no longer sent.
Examples
The four combinations below are real notifications, shortened for readability.
One event, one company
{
"alert": "annonces-legales", "part": 1, "parts": 1,
"events": [
{ "kind": "add", "data_type": "LEGAL_NOTICE", "siren": "449727205",
"name": "ARIANA PHARMACEUTICALS (449727205)", "id": "2b4690e8-…",
"object": { "date": "2026-09-08", "category": "Redressement judiciaire", "…": "…" }, "updates": [] }
]
}
Several events, one company
{
"alert": "collectives-procedures", "part": 1, "parts": 1,
"events": [
{ "kind": "update", "data_type": "COLLECTIVE_PROCEDURE", "siren": "891675829", "name": "FLEXIFAI (891675829)",
"id": "33b4b51a-…", "object": { "…": "…" },
"updates": [ { "after": { "administrators": [ { "type": "MANDATAIRE_JUDICIAIRE", "name": "Selarl Garnier-Guillouet…", "city": "Meaux" } ] },
"before": { "administrators": null } } ] },
{ "kind": "update", "data_type": "COLLECTIVE_PROCEDURE", "siren": "891675829", "name": "FLEXIFAI (891675829)",
"id": "5e70eb10-…", "object": { "…": "…" }, "updates": [ { "after": { "…": "…" }, "before": { "…": "…" } } ] }
]
}
One event, several companies
{
"alert": "annonces-legales", "part": 1, "parts": 1,
"events": [
{ "kind": "add", "data_type": "LEGAL_NOTICE", "siren": "911031409", "name": "LA COMPAGNIE DES INSECTES INDUSTRIE (911031409)", "id": "…", "object": { "…": "…" }, "updates": [] },
{ "kind": "add", "data_type": "LEGAL_NOTICE", "siren": "900276320", "name": "RFENCE (900276320)", "id": "…", "object": { "…": "…" }, "updates": [] },
{ "kind": "add", "data_type": "LEGAL_NOTICE", "siren": "884598038", "name": "ETSEME (884598038)", "id": "…", "object": { "…": "…" }, "updates": [] }
]
}
Several events, several companies
{
"alert": "annonces-legales", "part": 1, "parts": 1,
"events": [
{ "kind": "add", "data_type": "LEGAL_NOTICE", "siren": "847597143", "name": "MEDETIA (847597143)", "id": "…", "object": { "…": "…" }, "updates": [] },
{ "kind": "add", "data_type": "LEGAL_NOTICE", "siren": "480405687", "name": "SMARTLINE SYSTEMS (480405687)", "id": "…", "object": { "…": "…" }, "updates": [] },
{ "kind": "add", "data_type": "LEGAL_NOTICE", "siren": "480405687", "name": "SMARTLINE SYSTEMS (480405687)", "id": "…", "object": { "…": "…" }, "updates": [] },
{ "kind": "add", "data_type": "LEGAL_NOTICE", "siren": "480405687", "name": "SMARTLINE SYSTEMS (480405687)", "id": "…", "object": { "…": "…" }, "updates": [] },
{ "kind": "add", "data_type": "LEGAL_NOTICE", "siren": "480405687", "name": "SMARTLINE SYSTEMS (480405687)", "id": "…", "object": { "…": "…" }, "updates": [] }
]
}