Webhooks
Push-notificaties wanneer Corlega events plaatsvinden in je organisatie.
Webhooks sturen een HTTP POST naar jouw endpoint zodra een specifiek event plaatsvindt. bijvoorbeeld een nieuwe goedgekeurde offerte of een betaalde factuur.
Wanneer gebruik je webhooks
- Bestaand CRM bijwerken bij goedgekeurde offerte
- Notificatie naar Slack/Teams bij betaalde factuur
- Boekhouding-systeem syncen
- Custom-dashboards realtime updaten
Opzetten
Dashboard → Organisatie → Instellingen → Webhooks → Nieuwe endpoint- Vul de URL in (HTTPS verplicht)
- Kies de events waar je notificaties voor wilt ontvangen
- Kopieer de signing secret. wordt maar één keer getoond
- Test met de "Stuur test-event"-knop
Event types
| Event | Trigger |
|---|---|
quote.created | Concept-offerte aangemaakt |
quote.sent | Offerte verstuurd naar klant |
quote.approved | Klant heeft offerte goedgekeurd |
quote.rejected | Klant heeft offerte afgewezen |
invoice.created | Concept-factuur aangemaakt |
invoice.sent | Factuur verstuurd naar klant |
invoice.paid | Factuur volledig betaald |
contact.created | Nieuw contact aangemaakt (via UI, API of agent) |
contact.updated | Contact-gegevens gewijzigd |
customer_booking.confirmed | Klant heeft afspraak via Planning-agent bevestigd |
Payload-formaat
Elke webhook POST heeft dezelfde wrapper:
{
"event": "quote.approved",
"id": "evt_01HE8K2X4Q9...",
"timestamp": "2026-05-25T14:32:01.234Z",
"organizationId": "org_01HD...",
"data": {
"quoteId": "qte_01HE8K1Z0...",
"number": "Q-2026-0042",
"total": "1287.50",
"currency": "EUR",
"contact": {
"id": "con_01HC...",
"name": "Bouwbedrijf De Vries B.V."
},
"approvedAt": "2026-05-25T14:31:55.000Z"
}
}De data-payload verschilt per event-type. Zie de REST API reference voor de resource-shapes.
Signature verificatie
Elke request bevat de header X-Corlega-Signature: sha256=<hex>. een hex-encoded HMAC-SHA256 over de raw body met je signing-secret als sleutel.
Verifieer deze om man-in-the-middle of replay-attacks te voorkomen.
Node.js voorbeeld
import crypto from "node:crypto";
import express from "express";
const app = express();
function verifyCorlegaSignature(rawBody, signatureHeader, secret) {
if (!signatureHeader) return false;
const received = signatureHeader.replace(/^sha256=/, "");
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
if (expected.length !== received.length) return false;
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(received, "hex"),
);
}
// Belangrijk: raw body capture nodig voor signature-verificatie
app.post(
"/corlega-webhook",
express.raw({ type: "application/json" }),
(req, res) => {
const sig = req.headers["x-corlega-signature"];
const ok = verifyCorlegaSignature(
req.body,
sig,
process.env.CORLEGA_WEBHOOK_SECRET,
);
if (!ok) return res.status(401).send("Invalid signature");
const event = JSON.parse(req.body.toString());
// verwerk event…
res.status(200).send("OK");
},
);Next.js Route-handler voorbeeld
import crypto from "node:crypto";
import { NextResponse } from "next/server";
export const runtime = "nodejs";
export async function POST(req: Request) {
const rawBody = await req.text();
const sig = req.headers.get("x-corlega-signature");
const secret = process.env.CORLEGA_WEBHOOK_SECRET!;
const received = sig?.replace(/^sha256=/, "") ?? "";
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
if (
received.length !== expected.length ||
!crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(received, "hex"),
)
) {
return new NextResponse("Invalid signature", { status: 401 });
}
const event = JSON.parse(rawBody);
// verwerk event…
return NextResponse.json({ ok: true });
}Retries
Webhook-delivery retries bij niet-2xx-responses:
| Poging | Vertraging |
|---|---|
| 1 (initial) | direct |
| 2 | 1 minuut |
| 3 | 5 minuten |
| 4 | 30 minuten |
| 5 | 2 uur |
| 6 | 12 uur |
| 7 (laatste) | 24 uur |
Bij 3 opeenvolgende mislukte delivery-pogingen voor dezelfde endpoint krijg je een notificatie in het dashboard, en de endpoint wordt automatisch op inactive gezet na 7 dagen.
Idempotentie
Stuur dezelfde event-id meerdere keren? Behandel idempotent. Corlega kan dezelfde event meerdere keren leveren bij network-issues. Gebruik event.id als deduplicatie-sleutel.
if (await db.hasProcessed(event.id)) {
return res.status(200).send("Already processed");
}
await db.markProcessed(event.id);
await processEvent(event);
res.status(200).send("OK");Test events
In het dashboard kun je elk event-type handmatig triggeren met een test-payload. Handig voor development zonder een echte klant-actie te triggeren.
Volgende stap
- REST API. synchrone acties via HTTP
- Troubleshooting. webhook-issues debuggen