Webhooks
Een webhook van M.I.A. is een notificatie, geen datalevering: hij zegt dat een object gewijzigd is. De actuele toestand haal je op bij het endpoint in links.self. Zo blijft Mederi master en staat er geen tweede kopie van persoonsgegevens bij jou.
Payload
{
"event_uuid": "facade00-0000-4000-8000-00000000beef",
"event_type": "vacancy.updated",
"event_version": 1,
"occurred_at": "2026-09-09T10:00:00+02:00",
"entity": { "type": "vacancy", "id": "facade00-0000-4000-8000-000000000001" },
"links": { "self": "https://sandbox.mia.e-mederi.be/v1/vacancies/facade00-0000-4000-8000-000000000001" }
}Signatuur
Elke levering draagt vier headers. Verifieer de signatuur vóór je de payload gebruikt.
| X-MIA-Event-Id | Het event_uuid. |
| X-MIA-Event-Type | Het eventtype. |
| X-MIA-Timestamp | Unix seconds (UTC). |
| X-MIA-Signature | v1=<hex hmac_sha256(timestamp + "." + raw_body, secret)> |
De timestamp zit in de ondertekende tekst. Daardoor kan een onderschepte levering niet later opnieuw afgespeeld worden met een verse header. Weiger alles met een klokafwijking groter dan 300 seconden.
<?php
$body = file_get_contents("php://input");
$timestamp = (int) ($_SERVER["HTTP_X_MIA_TIMESTAMP"] ?? 0);
$signature = $_SERVER["HTTP_X_MIA_SIGNATURE"] ?? "";
// Replaycontrole: te oud of uit de toekomst = weigeren.
if (abs(time() - $timestamp) > 300) {
http_response_code(400);
exit;
}
$expected = "v1=" . hash_hmac("sha256", $timestamp . "." . $body, YOUR_WEBHOOK_SECRET);
// hash_equals: vergelijken zonder timingverschil.
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit;
}
http_response_code(204);import crypto from "node:crypto";
// LET OP: de ruwe body, niet het geparste object.
export function verify(rawBody, headers, secret) {
const timestamp = Number(headers["x-mia-timestamp"]);
const signature = headers["x-mia-signature"] ?? "";
if (!Number.isFinite(timestamp) ||
Math.abs(Date.now() / 1000 - timestamp) > 300) {
return false;
}
const expected = "v1=" + crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signature);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Eventcatalogus
| Event | Entiteit |
|---|---|
| vacancy.created | vacancy |
| vacancy.updated | vacancy |
| vacancy.deleted | vacancy |
| practice.created | practice |
| practice.updated | practice |
| practice.deleted | practice |
| candidate.created | candidate |
| application.created | application |
| recruitment_case.created | recruitment_case |
| system.ping | system |
Levering: at-least-once
M.I.A. levert at-least-once. Dedupliceer op X-MIA-Event-Id.
Een unieke index voorkomt een dubbele leveringsrij en een atomische claim voorkomt twee gelijktijdige workers op dezelfde levering. Wat geen van beide kan voorkomen: dat een worker crasht ná jouw endpoint bereikt te hebben, maar vóór het resultaat vastligt. Die levering wordt dan terecht opnieuw geprobeerd. Er bestaat geen transactie over een HTTP-call en een database heen, dus de keuze is niet "één keer of vaker" maar "misschien nul keer of minstens één keer" en een gemiste wijziging valt niet te repareren, een dubbele wel.
1. verifieer de signatuur
2. lees X-MIA-Event-Id
3. ken je dat id al? ja -> antwoord 2xx en doe verder niets
4. verwerk het event
5. leg het id vast (zelfde transactie als stap 4)Stap 3 antwoordt bewust 2xx: een duplicaat is geen fout, en een 4xx zou de levering onterecht als mislukt markeren.
Retries
Elke 2xx geldt als geslaagd. 4xx wordt niet opnieuw geprobeerd behalve 408, 425 en 429 want een consumer die 400 antwoordt, doet dat over zes uur weer. 5xx en timeouts wél.
| Poging | Wanneer |
|---|---|
| 1 | direct |
| 2 | +1 minuut |
| 3 | +5 minuuten |
| 4 | +15 minuuten |
| 5 | +1 uur |
| 6 | +6 uur |
| daarna | status dead; geen nieuwe pogingen |
Subscription beheren
curl -X POST "https://sandbox.mia.e-mederi.be/v1/webhooks" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://jouw-endpoint.example/mia/webhooks",
"event_types": ["vacancy.created", "vacancy.updated"]
}'Het antwoord bevat eenmalig secret. Bewaar het meteen; er is geen enkele manier om het opnieuw op te vragen.
curl -X POST "https://sandbox.mia.e-mederi.be/v1/webhooks/YOUR_WEBHOOK_UUID/test" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"