Ga naar hoofdinhoud

Webhooks & Event-notificaties

Webhooks stellen externe applicaties (zoals CRM-, ERP- of documentbeheersystemen) in staat om in real-time updates te ontvangen zodra een klant een formulier afrondt, een document ondertekent of klantgegevens wijzigt.


1. Werking & Triggermomenten

Sidefish verstuurt HTTP POST-verzoeken naar uw geconfigureerde webhook-URL op de volgende momenten:

  • Sessie Voltooid: Zodra een klant alle vragen van een formulier heeft ingevuld en ingediend.
  • Document Ondertekend: Wanneer alle betrokken partijen een document rechtsgeldig hebben ondertekend via itsme, eID of SMS.
  • Klantgegevens Gewijzigd: Wanneer antwoorden in het formulier afwijken van de reeds bekende gegevens op de klantenfiche (bijvoorbeeld een verhuizing of gewijzigd telefoonnummer).
  • Handmatige Trigger: Wanneer een medewerker op de knop Webhook verzenden klikt in de klantenfiche of sessiedetails.

2. Webhook Configureren

Webhooks worden ingesteld op organisatieniveau via Organisaties > Integraties > Webhooks:

  1. Voer de Doel-URL van uw eigen server of webhook-ontvanger in.
  2. Vul een Geheime Sleutel (Secret) in voor cryptografische handtekeningverificatie.
  3. Gebruik optioneel dynamische URL-placeholders.

URL-Placeholders

Sidefish vervangt onderstaande variabelen automatisch in de doel-URL vóór verzending:

PlaceholderBeschrijvingVoorbeeldwaarde
{{sessionId}}Het unieke ID van de afgeronde sessie.64e8b1c4e12a9c001f5a9e31
{{customerId}}Het interne database-ID van het contact.64e8b1c4e12a9c001f5a9e32
{{customer.thirdpartyId}}Het externe CRM-klantnummer (indien aanwezig).CUST-98421
{{signedDocumentFileId}}De unieke bestandsreferentie van het ondertekende PDF-document.doc_78f192b8

Voorbeeld doel-URL:

https://api.uwkantoor.be/webhooks/sidefish?session={{sessionId}}&dossier={{customer.thirdpartyId}}

3. Beveiliging: HMAC-SHA256 Handtekeningverificatie

Om te verifiëren dat een inkomend webhook-verzoek daadwerkelijk afkomstig is van Sidefish en onderweg niet is gewijzigd, berekent Sidefish een HMAC-SHA256 handtekening op basis van de ruwe JSON-payload en uw geheime sleutel.

Deze handtekening wordt meegestuurd in de HTTP-header:

X-Sidefish-Signature: a3f89e2c45b7610d9e... (64-teken hexadecimale hash)

Verificatievoorbeeld Node.js / Express

import express, { Request, Response } from "express";
import * as crypto from "crypto";

const app = express();
const WEBHOOK_SECRET = process.env.SIDEFISH_WEBHOOK_SECRET!;

// Let op: gebruik express.raw() of express.json({ verify: ... }) om de ongewijzigde buffer te bewaren
app.post(
"/webhooks/sidefish",
express.raw({ type: "application/json" }),
(req: Request, res: Response) => {
const signatureHeader = req.header("X-Sidefish-Signature");
const rawBody = req.body.toString("utf8");

if (!signatureHeader) {
return res.status(401).send("Handtekening ontbreekt");
}

const calculatedSignature = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(rawBody)
.digest("hex");

// Tijdsonafhankelijke vergelijking ter bescherming tegen timing attacks
const isValid = crypto.timingSafeEqual(
Buffer.from(signatureHeader, "utf-8"),
Buffer.from(calculatedSignature, "utf-8")
);

if (!isValid) {
return res.status(403).send("Ongeldige handtekening");
}

const payload = JSON.parse(rawBody);
console.log("Geverifieerde webhook ontvangen voor sessie:", payload.sessionId);

// Bevestig ontvangst met 200 OK
res.status(200).json({ received: true });
}
);

app.listen(3000);

Verificatievoorbeeld Python (Flask)

import hmac
import hashlib
import os
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get("SIDEFISH_WEBHOOK_SECRET", "").encode("utf-8")

@app.route("/webhooks/sidefish", methods=["POST"])
def handle_sidefish_webhook():
signature_header = request.headers.get("X-Sidefish-Signature")
raw_payload = request.get_data()

if not signature_header:
return "Handtekening ontbreekt", 401

computed_signature = hmac.new(WEBHOOK_SECRET, raw_payload, hashlib.sha256).hexdigest()

if not hmac.compare_digest(signature_header, computed_signature):
return "Ongeldige handtekening", 403

data = request.get_json()
print(f"Webhook ontvangen voor klant: {data.get('customerId')}")

return jsonify({"received": True}), 200

if __name__ == "__main__":
app.run(port=3000)

4. Payload Structuur

De webhook-payload bevat gestructureerde data over de klant, het dossier, eventuele adres- of gegevenswijzigingen en directe downloadlinks naar documenten en uploads.

JSON Voorbeeld

{
"sessionId": "64e8b1c4e12a9c001f5a9e31",
"customerId": "64e8b1c4e12a9c001f5a9e32",
"hasCustomerDataChanged": true,
"changedFields": [
"Straat",
"Huisnummer",
"Postcode"
],
"customer": {
"id": "64e8b1c4e12a9c001f5a9e32",
"thirdpartyId": "BROKER-4482",
"hasCustomerDataChanged": true,
"hasAddressChanged": true,
"changedFields": [
"Straat",
"Huisnummer",
"Postcode"
],
"data": {
"Voornaam": "Pieter",
"Achternaam": "Claes",
"Email": "pieter.claes@example.be",
"Telefoon": "+32470123456",
"Straat": "Kerkstraat",
"Huisnummer": "14",
"Postcode": "9000",
"Gemeente": "Gent",
"Rijksregisternummer": "85.04.12-123.45"
}
},
"portfolioOwner": {
"firstname": "Thomas",
"lastname": "Vermeulen",
"email": "thomas@makelaarspraktijk.be",
"organisationName": "Makelaarspraktijk Vermeulen"
},
"files": [
{
"id": "doc_64e8b20a",
"type": "document",
"filename": "Aanvraag_Woning_Pieter_Claes.pdf",
"displayName": "Rapport Woningverzekering",
"url": "https://sidefish.app/files/downloads/doc_64e8b20a.pdf",
"signedUrl": "https://sidefish.app/files/signed/doc_64e8b20a_signed.pdf"
},
{
"id": "upload_99f12a",
"type": "upload",
"filename": "schadefoto_dak.jpg",
"url": "https://sidefish.app/files/uploads/upload_99f12a.jpg"
}
],
"session": {
"id": "64e8b1c4e12a9c001f5a9e31",
"uuid": "8d3e91a0-4f9e-4a62-9b21-44bb519e1c14",
"hasCustomerDataChanged": true,
"createdAt": "2026-09-13T14:20:00.000Z",
"updatedAt": "2026-09-13T14:32:15.000Z"
}
}

Belangrijke Velden

  • hasCustomerDataChanged: Geeft direct aan of de klant gegevens heeft ingevuld die afwijken van de initiële fiche.
  • hasAddressChanged: Specifieke vlag voor adresaanpassingen (handig om in het CRM een automatische verhuistaak aan te maken).
  • changedFields: Array met de exacte veldnamen die gewijzigd zijn.
  • files: Bevat alle relevante documenten met een permanente download-URL:
    • type: "document": Door Sidefish gegenereerd rapport of contract. Indien ondertekend bevat signedUrl het rechtsgeldig getekende bestand.
    • type: "upload": Bestanden of foto's die de klant zelf tijdens het invullen heeft toegevoegd.

5. Retry-beleid & Fouttolerantie

Sidefish hanteert een robuust afleveringsmechanisme:

  • Verwachte HTTP Response: Uw endpoint moet antwoorden met een HTTP-statuscode in de reeks 200-299 (bijvoorbeeld 200 OK).
  • Timeout: Sidefish wacht maximaal 15 seconden op een reactie van uw server.
  • Automatische Herpogingen (Retries):
    • Bij een netwerkfout, timeout of HTTP-status $\ge 400$ probeert het platform de webhook maximaal 3 keer af te leveren.
    • Tussen de pogingen geldt een oplopende wachttijd (1 seconde na poging 1, 2 seconden na poging 2).
  • Monitoring & Logging: Alle verzendstatussen, responscodes en latenties worden gemonitord en gelogd in het organisatielogboek.