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.
| Method | Pad | Beschrijving |
|---|---|---|
| GET | /api/orgs/:orgId/webhooks | Webhooks ophalen |
| GET | /api/orgs/:orgId/webhooks/:id | Webhook op ID ophalen |
| POST | /api/orgs/:orgId/webhooks | Webhook aanmaken |
| PATCH | /api/orgs/:orgId/webhooks/:id | Webhook bijwerken |
| DELETE | /api/orgs/:orgId/webhooks/:id | Webhook 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 Instellingen → Ontwikkelaars bij het aanmaken van een webhook. - enabled -
trueom levering in te schakelen
Webhook-payload
Elke webhook-levering is een POST met:
Headers:
Content-Type: application/jsonX-Webhook-Event-ID- stabiele unieke ID voor deze logische levering (zelfde waarde bij retries)X-Webhook-Signature- HMAC-SHA256 handtekening van de body, formaatsha256=<hex>(zie Handtekening verifiëren)
Body:
{
"event": "invoice:created",
"data": {},
"timestamp": 1735689600000
}| Veld | Type | Beschrijving |
|---|---|---|
event | string | Event key (bijv. invoice:created) |
data | object | Event-specifieke payload |
timestamp | number | Unix timestamp (ms) wanneer het event werd uitgezonden |
Zie Voorbeelden voor volledige payload-voorbeelden.
Handtekening verifiëren
Elke webhook bevat een eigen signing secret (64 hex-karakters, getoond bij aanmaken in Instellingen → 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:
- Bereken
HMAC-SHA256(signingSecret, raw_request_body)(lowercase hex). - Stuur dat als
X-Webhook-Signature: sha256=<hex>.
Hoe jij verifieert:
- Lees de ruwe request-body (vóór JSON-parsen — anders matcht de hash niet).
- Bereken zelf
HMAC-SHA256(signingSecret, raw_body)in hex. - Vergelijk met de waarde achter
sha256=uit deX-Webhook-Signatureheader 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 wordt eenmalig getoond bij aanmaken. 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).
| Poging | Wachttijd sinds vorige poging |
|---|---|
| 1 | direct |
| 2 | 1 seconde |
| 3 | 2 seconden |
| 4 | 4 seconden |
| 5 | 8 seconden |
| 6 | 5 minuten |
| 7 | 30 minuten |
| 8 | 2 uur |
| 9 | 6 uur |
| 10 | 24 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.
Event types
Event keys gebruiken het formaat entity:action of entity:subresource:action. Hieronder een selectie; de volledige lijst staat in Tillor onder Instellingen → Ontwikkelaars - in Tillor: Instellingen → Ontwikkelaars (per organisatie) bij het aanmaken van een webhook.
| Event Key | Beschrijving |
|---|---|
* (wildcard) | Abonneer op alle events |
accessLogEntry:coupled | Toegangslog-entry gekoppeld aan klant of toegangsmethode |
accessLogEntry:processed | Toegangslog-entry verwerkt |
accessMethod:created | Toegangsmethode aangemaakt |
accessMethod:deleted | Toegangsmethode verwijderd |
accessMethod:updated | Toegangsmethode bijgewerkt |
accessMethodOperation:created | Toegangsmethode-operatie aangemaakt |
accessMethodOperation:updated | Toegangsmethode-operatie bijgewerkt |
barrier:created | Barrière aangemaakt |
barrier:deleted | Barrière verwijderd |
barrier:updated | Barrière bijgewerkt |
call:created | Gesprek aangemaakt |
call:updated | Gesprek bijgewerkt |
comment:created | Reactie aangemaakt |
comment:deleted | Reactie verwijderd |
comment:mentioned | Gebruiker genoemd in reactie |
comment:pinned | Reactie vastgepind |
comment:resolved | Reactie opgelost |
comment:updated | Reactie bijgewerkt |
controller:adoption:approved | Controller-adoptie goedgekeurd |
controller:adoption:rejected | Controller-adoptie afgewezen |
controller:adoption:requested | Controller-adoptie aangevraagd |
controller:created | Controller aangemaakt |
controller:deleted | Controller verwijderd |
controller:logs:submitted | Controller-logs ingediend |
controller:updated | Controller bijgewerkt |
customer:contact:created | Klantcontact aangemaakt |
customer:contact:deleted | Klantcontact verwijderd |
customer:contact:updated | Klantcontact bijgewerkt |
customer:created | Klant aangemaakt |
customer:deleted | Klant verwijderd |
customer:updated | Klant bijgewerkt |
document:created | Document aangemaakt |
document:deleted | Document verwijderd |
document:updated | Document bijgewerkt |
event-log:created | Eventlog-entry aangemaakt |
event-log:updated | Eventlog bijgewerkt |
identityDocumentScan:completed | Identiteitsdocument-scan voltooid |
identityDocumentScan:deleted | Identiteitsdocument-scan verwijderd |
identityDocumentScan:linked | Identiteitsdocument-scan gekoppeld aan klant |
identityDocumentScan:unlinked | Identiteitsdocument-scan ontkoppeld van klant |
invoice:created | Factuur aangemaakt |
invoice:deleted | Factuur verwijderd |
invoice:paid | Factuur betaald |
invoice:updated | Factuur bijgewerkt |
mandate:created | Machtiging aangemaakt |
mandate:deleted | Machtiging verwijderd |
mandate:updated | Machtiging bijgewerkt |
nfc-tag:blocked | NFC-tag geblokkeerd |
nfc-tag:presented | NFC-tag gepresenteerd |
nfc-tag:unblocked | NFC-tag gedeblokkeerd |
nfc-tag:updated | NFC-tag bijgewerkt |
notification-delivery:updated | Notificatielevering bijgewerkt |
payment-group:assigned-to-invoice | Betalingsgroep aan factuur gekoppeld |
payment-group:created | Betalingsgroep aangemaakt |
payment-group:deleted | Betalingsgroep verwijderd |
payment-group:payments-added | Betalingen toegevoegd aan betalingsgroep |
payment-group:payments-removed | Betalingen verwijderd uit betalingsgroep |
payment-group:unassigned-from-invoice | Betalingsgroep ontkoppeld van factuur |
payment-group:updated | Betalingsgroep bijgewerkt |
payment-report:updated | Betalingsrapport bijgewerkt |
payment:updated | Betaling bijgewerkt |
pdf:clear-ipad | PDF gewist van iPad |
pdf:to-ipad | PDF naar iPad verzonden |
reservation:created | Reservering aangemaakt |
reservation:updated | Reservering bijgewerkt |
Webhooks vs SSE
| Use case | Aanbeveling |
|---|---|
| Server-side integratie | Webhooks - Jouw server ontvangt POSTs |
| Eenvoudige logging | Webhooks - Minimale setup |
| Realtime dashboard, live updates in app | SSE - Eén verbinding, lage latentie. |
| Veel event types, client-side filteren | SSE - Gebruik events query-param om verkeer te beperken. |