Tillor
Ontwikkelaars

Webhooks

Ontvang realtime events via HTTP POST naar jouw endpoint

Webhooks ontvangen realtime events via HTTP POST naar een door jou geconfigureerde URL. Dezelfde events zijn ook beschikbaar via SSE. Webhooks zijn per organisatie geconfigureerd - elke organisatie heeft zijn eigen webhooks.

Beheer

Zie de OpenAPI-specificatie voor volledige request- en response-schema's.

MethodPadBeschrijving
GET/api/orgs/:orgId/webhooksWebhooks ophalen
GET/api/orgs/:orgId/webhooks/delivery-statsLeveringsstatistieken ophalen
POST/api/orgs/:orgId/webhooks/retry-deliveriesLeveringen opnieuw proberen. Body: mode (failed of force) en optioneel id voor één webhook. failed: alleen definitief mislukte leveringen, direct. force: wachtende en definitief mislukte leveringen op de achtergrond, negeert wachttijden.
GET/api/orgs/:orgId/webhooks/:idWebhook op ID ophalen
POST/api/orgs/:orgId/webhooksWebhook aanmaken
PATCH/api/orgs/:orgId/webhooks/:idWebhook bijwerken
DELETE/api/orgs/:orgId/webhooks/:idWebhook verwijderen

Webhook aanmaken

POST /api/orgs/org_abc123/webhooks
Content-Type: application/json
x-api-key: tkn_xxx
X-Tillor-Org-Id: org_abc123
{
  "url": "https://jouw-server.com/webhooks/tillor",
  "subscribedEventKeys": ["invoice:created", "invoice:paid", "customer:updated"],
  "enabled": true
}
  • url - HTTPS-endpoint die POST-requests accepteert
  • subscribedEventKeys - Array van event keys of ["*"] voor alle events. De volledige lijst staat in Tillor onder Profielmenu → Ontwikkelaars bij Webhook toevoegenGeabonneerde gebeurtenissen.
  • enabled - true om levering in te schakelen

Je mag alleen events kiezen waarvoor je organisatielid de juiste permissie heeft (bijv. bankoverschrijvingen voor bankAccountTransaction:*). Wildcard * is alleen beschikbaar voor leden met volledige toegang (*). Bij een ongeldige combinatie geeft de API een foutmelding.


Webhook-payload

Elke webhook-levering is een POST met:

Headers:

  • Content-Type: application/json
  • X-Webhook-Event-ID - stabiele unieke ID voor deze logische levering (zelfde waarde bij retries)
  • X-Webhook-Signature - HMAC-SHA256 handtekening van de body, formaat sha256=<hex> (zie Handtekening verifiëren)

Body:

{
  "event": "invoice:created",
  "data": {},
  "timestamp": 1735689600000,
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
  "spanId": "00f067aa0ba902b7"
}
VeldTypeBeschrijving
eventstringEvent key (bijv. invoice:created)
dataobjectEvent-specifieke payload
timestampnumberUnix timestamp (ms) wanneer het event werd uitgezonden
traceIdstringOpenTelemetry trace-id van de Tillor-actie die dit event veroorzaakte. Geef deze door bij support; het is geen geheim.
spanIdstring (optioneel)Span binnen die trace

Zie Voorbeelden voor volledige payload-voorbeelden.


Handtekening verifiëren

Elke webhook bevat een eigen signing secret (64 hex-karakters, zichtbaar in de webhooktabel via Ondertekeningssleutel tonen op Profielmenu → Ontwikkelaars). Tillor ondertekent elke POST met die secret zodat jij kunt verifiëren dat het verzoek écht van Tillor komt en dat de body niet is aangepast.

Hoe Tillor ondertekent:

  1. Bereken HMAC-SHA256(signingSecret, raw_request_body) (lowercase hex).
  2. Stuur dat als X-Webhook-Signature: sha256=<hex>.

Hoe jij verifieert:

  1. Lees de ruwe request-body (vóór JSON-parsen - anders matcht de hash niet).
  2. Bereken zelf HMAC-SHA256(signingSecret, raw_body) in hex.
  3. Vergelijk met de waarde achter sha256= uit de X-Webhook-Signature header met een constant-time vergelijking.
import crypto from "node:crypto";

function verifyTillorWebhook(rawBody, headerSignature, signingSecret) {
  const expected = crypto.createHmac("sha256", signingSecret).update(rawBody).digest("hex");
  const received = headerSignature.replace(/^sha256=/, "");
  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(received, "hex");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Behandel de secret als wachtwoord

De signing secret kun je in Tillor tonen via Ondertekeningssleutel tonen in de webhooktabel. Sla 'm op als secret (env var, vault) en log 'm nooit. Vermoed je een lek? Verwijder de webhook en maak een nieuwe aan; daarmee krijg je een nieuwe secret.


Leveringsgedrag en retries

  • Antwoord met een 2xx-status om succes te bevestigen.
  • Bij een mislukte levering (niet-2xx, netwerkfout of geblokkeerd doel) probeert Tillor de levering automatisch opnieuw met steeds langere tussenpozen. Elke poging hergebruikt dezelfde payload, dezelfde handtekening en hetzelfde event-id (idempotent).
PogingWachttijd sinds vorige poging
1direct
21 seconde
32 seconden
44 seconden
58 seconden
65 minuten
730 minuten
82 uur
96 uur
1024 uur
  • Slaagt één van de pogingen (2xx) → de levering is afgerond.
  • Slaagt ook poging 10 (na 24u) niet → de levering wordt definitief gemarkeerd als mislukt en niet verder geprobeerd.
  • Wordt de webhook intussen uitgeschakeld, dan stopt Tillor met opnieuw proberen.

Idempotentie

Elke retry hergebruikt dezelfde X-Webhook-Event-ID en X-Webhook-Signature. Dedupliceer aan jouw kant op die header zodat een succesvolle retry na een trage 200 geen dubbele verwerking veroorzaakt.

Meldingen bij aanhoudende fouten

Als leveringen voor een webhook-endpoint blijven mislukken, stuurt Tillor maximaal twee meldingen per incident (per endpoint):

  1. Na 5 minuten aanhoudende fouten - e-mail naar het organisatie-e-mailadres (supportEmail, anders email) en naar organisatieleden met rechten om organisatie-instellingen te wijzigen, plus push (indien ingeschakeld) voor die leden. De e-mail vermeldt sinds wanneer de leveringen mislukken. Datums in de e-mail gebruiken de tijdzone uit Instellingen > Algemeen (standaard Europe/Brussels).
  2. Bij definitieve mislukking - dezelfde ontvangers krijgen één melding wanneer het eerste event in dat incident na alle pogingen definitief mislukt is gemarkeerd.

Je krijgt geen melding per mislukt event. Zodra alle mislukte leveringen voor een endpoint zijn opgelost (succesvol afgeleverd), start een volgend incident opnieuw met dezelfde regels.

Op Profielmenu → Ontwikkelaars toont de webhooktabel per endpoint een compacte leveringsstatus (aantallen wachtend op opnieuw proberen en definitief mislukt). Klik op het -icoon voor details, inclusief de volledige foutmelding en de payload van de meest recente poging.

  • Mislukte opnieuw proberen (↻): alleen definitief mislukte leveringen voor die webhook.
  • Forceer opnieuw proberen (⚡): alle wachtende én definitief mislukte leveringen voor die webhook, ongeacht geplande wachttijden. Tillor start dit op de achtergrond zodat grote wachtrijen (bijv. duizenden events) veilig verwerkt worden.
  • Alles forceer opnieuw proberen rechtsboven naast Webhook toevoegen: hetzelfde voor alle ingeschakelde webhooks in de organisatie.

Event types

Event keys gebruiken het formaat entity:action of entity:subresource:action. Hieronder de volledige lijst; dezelfde keys staan in Tillor onder Profielmenu → Ontwikkelaars bij Webhook toevoegenGeabonneerde gebeurtenissen.

Event KeyBeschrijving
* (wildcard)Alle events (alleen met volledige org-permissie)
accessLogEntry:coupledToegangslog-entry gekoppeld aan klant of toegangsmethode
accessLogEntry:processedToegangslog-entry verwerkt
accessMethod:createdToegangsmethode aangemaakt
accessMethod:deletedToegangsmethode verwijderd
accessMethod:updatedToegangsmethode bijgewerkt
accessMethodOperation:createdToegangsmethode-operatie aangemaakt
accessMethodOperation:updatedToegangsmethode-operatie bijgewerkt
barrier:createdBarrière aangemaakt
barrier:deletedBarrière verwijderd
barrier:updatedBarrière bijgewerkt
bankAccountTransaction:importedBanktransacties geïmporteerd of gesynchroniseerd
call:createdGesprek aangemaakt
call:updatedGesprek bijgewerkt
comment:createdReactie aangemaakt
comment:deletedReactie verwijderd
comment:mentionedGebruiker genoemd in reactie
comment:pinnedReactie vastgepind
comment:resolvedReactie opgelost
comment:updatedReactie bijgewerkt
controller:adoption:approvedController-adoptie goedgekeurd
controller:adoption:rejectedController-adoptie afgewezen
controller:adoption:requestedController-adoptie aangevraagd
controller:createdController aangemaakt
controller:deletedController verwijderd
controller:logs:submittedController-logs ingediend
controller:updatedController bijgewerkt
customer:contact:createdKlantcontact aangemaakt
customer:contact:deletedKlantcontact verwijderd
customer:contact:updatedKlantcontact bijgewerkt
customer:createdKlant aangemaakt
customer:deletedKlant verwijderd
customer:updatedKlant bijgewerkt
document:createdDocument aangemaakt
document:deletedDocument verwijderd
document:updatedDocument bijgewerkt
event-log:createdEventlog-entry aangemaakt
event-log:updatedEventlog bijgewerkt
identityDocumentScan:completedIdentiteitsdocument-scan voltooid
identityDocumentScan:deletedIdentiteitsdocument-scan verwijderd
identityDocumentScan:linkedIdentiteitsdocument-scan gekoppeld aan klant
identityDocumentScan:pendingQueueChangedWachtrij met openstaande scans gewijzigd
identityDocumentScan:unlinkedIdentiteitsdocument-scan ontkoppeld van klant
inbox:message-createdNieuw inbox-bericht (inkomend of uitgaand)
inbox:thread-updatedInbox-gesprek bijgewerkt (preview, archief, laatste bericht)
invoice:createdFactuur aangemaakt
invoice:deletedFactuur verwijderd
invoice:paidFactuur betaald
invoice:updatedFactuur bijgewerkt
mandate:createdMachtiging aangemaakt
mandate:deletedMachtiging verwijderd
mandate:updatedMachtiging bijgewerkt
meter-alarm:createdMeteralarm aangemaakt
meter-alarm:resolvedMeteralarm opgelost
meter:updatedMeter bijgewerkt
nfc-tag:blockedNFC-tag geblokkeerd
nfc-tag:presentedNFC-tag gepresenteerd
nfc-tag:unblockedNFC-tag gedeblokkeerd
nfc-tag:updatedNFC-tag bijgewerkt
notification-delivery:updatedNotificatielevering bijgewerkt
payment-group:assigned-to-invoiceBetalingsgroep aan factuur gekoppeld
payment-group:createdBetalingsgroep aangemaakt
payment-group:deletedBetalingsgroep verwijderd
payment-group:payments-addedBetalingen toegevoegd aan betalingsgroep
payment-group:payments-removedBetalingen verwijderd uit betalingsgroep
payment-group:unassigned-from-invoiceBetalingsgroep ontkoppeld van factuur
payment-group:updatedBetalingsgroep bijgewerkt
payment-report:updatedBetalingsrapport bijgewerkt
payment:createdBetaling aangemaakt
payment:deletedBetaling verwijderd
payment:updatedBetaling bijgewerkt
img:clear-tabletAfbeelding gewist van tablet
img:to-tabletAfbeelding naar tablet verzonden
pdf:clear-tabletPDF gewist van tablet
pdf:to-tabletPDF naar tablet verzonden
print-queue-job:updatedPrintwachtrijtaak bijgewerkt
reservation:createdReservering aangemaakt
reservation:updatedReservering bijgewerkt
terrain:updatedTerrein bijgewerkt

Webhooks vs SSE

Use caseAanbeveling
Server-side integratieWebhooks - Jouw server ontvangt POSTs
Eenvoudige loggingWebhooks - Minimale setup
Realtime dashboard, live updates in appSSE - Eén verbinding, lage latentie.
Veel event types, client-side filterenSSE - Gebruik events query-param om verkeer te beperken.

Gerelateerd

  • OpenAPI - Volledige API-specificatie
  • HTTP API - REST API en API-sleutels
  • SSE - Zelfde events via Server-Sent Events

On this page