In deze documentatie

Uitgaande webhooks

Webhooks sturen Manisma-gebeurtenissen naar een publieke HTTPS-endpoint van jouw organisatie.

Endpoint instellen

Open als Admin Beheer → Koppelingen, maak een endpoint en kies naam, URL en gebeurtenissen. Het signing secret wordt één keer getoond. Bewaar het veilig. Bij rotatie geldt het nieuwe secret vanaf het opslaan.

De URL moet:

  • HTTPS gebruiken;
  • publiek via DNS bereikbaar zijn;
  • poort 443 of 8443 gebruiken;
  • geen gebruikersnaam/wachtwoord in de URL bevatten;
  • niet naar localhost, privé-IP, link-local of cloudmetadata wijzen.

Gebruik Test om een ping te sturen voordat je echte gebeurtenissen activeert.

Gebeurtenissen

Gebeurtenis Trigger
ping Handmatige endpointtest.
client.created Klant aangemaakt.
client.updated Klant gewijzigd.
job.created Opdracht aangemaakt.
job.planned Opdrachtstatus gewijzigd naar gepland.
job.completed Opdrachtstatus gewijzigd naar voltooid.
job.cancelled Opdrachtstatus gewijzigd naar geannuleerd.
approval.approved Klant heeft een voorstel goedgekeurd.
approval.declined Klant heeft een voorstel afgewezen.
workorder.completed Facturatieklare werkbongegevens bij voltooiing.

Een wijziging aan een ander veld van een al voltooide opdracht verstuurt job.completed niet opnieuw; de status zelf moet veranderen.

Envelope en headers

{
  "event": "job.completed",
  "version": 1,
  "tenant_id": "tenant-uuid",
  "occurred_at": "2026-07-13T09:15:00.000Z",
  "data": {}
}

Headers:

  • X-Manisma-Event: gebeurtenisnaam;
  • X-Manisma-Delivery: stabiel id van deze logische levering;
  • X-Manisma-Signature: sha256=<hex HMAC-SHA256> over de exacte ruwe body.

Verifieer de HMAC vóór JSON-parsing en gebruik een timing-safe vergelijking:

import { createHmac, timingSafeEqual } from 'node:crypto'

export function verify(rawBody, header, secret) {
  if (!header?.startsWith('sha256=')) return false
  const expected = createHmac('sha256', secret).update(rawBody).digest('hex')
  const supplied = header.slice(7)
  const a = Buffer.from(expected, 'hex')
  const b = Buffer.from(supplied, 'hex')
  return a.length === b.length && timingSafeEqual(a, b)
}

Payloads

Klantgebeurtenissen bevatten id, customer_number, name, client_type, email en phone. Interne klantnotities ontbreken.

Opdrachtgebeurtenissen bevatten opdracht-id/-nummer, titel, status, prioriteit, planning en compacte objecten voor klant, serviceadres en hoofdtechnieker. Een niet-toegewezen of adresloze opdracht gebruikt null.

Goedkeuringsgebeurtenissen bevatten goedkeurings-id, job_id, status, gekozen slot en antwoordtijd. chosen_slot kan een JSON-string of oudere vrije tekst zijn; parse defensief.

workorder.completed bevat:

  • compacte opdracht en klant;
  • het meest recente voltooide bezoek, indien beschikbaar;
  • gebruikte materialen met SKU, verkoopprijs, hoeveelheid en lijnbedrag;
  • werkduur;
  • materiaaltotaal in EUR.

De materiaalprijs is de verkoopprijs op het moment dat de webhookpayload wordt opgebouwd, niet noodzakelijk een historisch vastgeklikte prijs.

Levering en retries

Webhooks zijn at least once. Maak je ontvanger idempotent op X-Manisma-Delivery. Een succesvolle ontvangst is iedere 2xx-respons. Redirects worden niet gevolgd en iedere poging heeft een timeout van 10 seconden.

Bij een tijdelijke fout zijn er maximaal drie pogingen: direct, na 60 seconden en nogmaals 300 seconden later. Dezelfde delivery-id blijft behouden. Retries draaien momenteel in het serviceproces en overleven geen redeploy; controleer daarom ook het leveringslog in Koppelingen op achtergebleven pending-regels.

Sla eerst het delivery-id op in dezelfde transactie als je verwerking en antwoord daarna snel met 2xx. Verplaats trage verwerking naar je eigen queue.