SANDBOX — synthetische gegevens, geen productiedata
Sandbox

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

POST naar jouw endpoint
{
  "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-IdHet event_uuid.
X-MIA-Event-TypeHet eventtype.
X-MIA-TimestampUnix seconds (UTC).
X-MIA-Signaturev1=<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.

Verificatie (PHP)
<?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);
Verificatie (Node)
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

EventEntiteit
vacancy.createdvacancy
vacancy.updatedvacancy
vacancy.deletedvacancy
practice.createdpractice
practice.updatedpractice
practice.deletedpractice
candidate.createdcandidate
application.createdapplication
recruitment_case.createdrecruitment_case
system.pingsystem

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.

Wat jouw endpoint moet doen
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.

PogingWanneer
1direct
2+1 minuut
3+5 minuuten
4+15 minuuten
5+1 uur
6+6 uur
daarnastatus dead; geen nieuwe pogingen

Subscription beheren

Aanmaken
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.

Testevent
curl -X POST "https://sandbox.mia.e-mederi.be/v1/webhooks/YOUR_WEBHOOK_UUID/test" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Je endpoint moet publiek en over https bereikbaar zijn. URL's die naar localhost, een privaat netwerk, een link-local adres of een cloud-metadata-adres wijzen, worden geweigerd ook wanneer een hostnaam daar pas via DNS naartoe wijst. Redirects worden niet gevolgd.