Webhooks verwerken en handtekening verifiëren
Wesender stuurt realtime-events naar jouw endpoint zodra een e-mail bezorgd, geopend of gebounced wordt. Met webhooks kun je je database automatisch bijwerken zonder polling.
Beschikbare events
| Event | Betekenis |
|---|---|
email.delivered |
E-mail succesvol bezorgd bij de ontvanger |
email.bounced |
E-mail permanent geweigerd (hard bounce) |
email.soft_bounced |
E-mail tijdelijk geweigerd (probeer opnieuw) |
email.opened |
Ontvanger heeft de e-mail geopend |
email.clicked |
Ontvanger heeft een link aangeklikt |
email.complained |
Ontvanger heeft de e-mail als spam gemarkeerd |
Stap 1: Webhook aanmaken in het dashboard
- Ga naar app.wesender.nl → Webhooks → Nieuw
- Vul je publiek bereikbare endpoint-URL in (bijv.
https://mijnapp.nl/api/webhooks/email) - Selecteer de events die je wilt ontvangen
- Kopieer de geheime sleutel — je hebt die nodig voor handtekeningverificatie
Stap 2: Handtekening verifiëren
Verifieer altijd de handtekening voordat je een event verwerkt. Dit voorkomt nep-verzoeken.
Node.js / Express
import express from "express"
import crypto from "crypto"
const app = express()
const SECRET = process.env.WESENDER_WEBHOOK_SECRET!
// Raw body is nodig voor HMAC-verificatie
app.use("/api/webhooks/email", express.raw({ type: "application/json" }))
app.post("/api/webhooks/email", (req, res) => {
const signature = req.headers["x-wesender-signature"] as string
const body = req.body as Buffer
const verwacht = crypto
.createHmac("sha256", SECRET)
.update(body)
.digest("hex")
if (signature !== verwacht) {
return res.status(401).json({ error: "Ongeldige handtekening" })
}
const event = JSON.parse(body.toString())
verwerkEvent(event)
// Altijd snel 200 teruggeven — Wesender probeert anders opnieuw
res.json({ ok: true })
})
Next.js App Router
// app/api/webhooks/email/route.ts
import { NextRequest, NextResponse } from "next/server"
import crypto from "crypto"
const SECRET = process.env.WESENDER_WEBHOOK_SECRET!
export async function POST(req: NextRequest) {
const body = await req.arrayBuffer()
const bodyBytes = Buffer.from(body)
const signature = req.headers.get("x-wesender-signature") ?? ""
const verwacht = crypto
.createHmac("sha256", SECRET)
.update(bodyBytes)
.digest("hex")
if (signature !== verwacht) {
return NextResponse.json({ error: "Ongeldige handtekening" }, { status: 401 })
}
const event = JSON.parse(bodyBytes.toString())
await verwerkEvent(event)
return NextResponse.json({ ok: true })
}
Stap 3: Events verwerken
async function verwerkEvent(event: { type: string; data: Record<string, unknown> }) {
switch (event.type) {
case "email.delivered":
// Markeer e-mail als bezorgd in je database
await db.emails.update({ id: event.data.id, status: "delivered" })
break
case "email.bounced":
// Voeg toe aan suppressielijst, stuur geen mails meer
await db.suppressions.create({ email: event.data.to, reason: "bounce" })
break
case "email.complained":
// Ontvanger markeerde als spam — verwijder uit alle lijsten
await db.suppressions.create({ email: event.data.to, reason: "complaint" })
break
case "email.opened":
console.log("Geopend door:", event.data.to)
break
}
}
Lokaal testen met ngrok
- Installeer ngrok:
npm install -g ngrok - Start een tunnel:
ngrok http 3000 - Kopieer de
https://xxxx.ngrok.io-URL - Vul die in als webhook-URL in het Wesender-dashboard
Tips
- Geef altijd snel een 200-respons terug — Wesender probeert anders tot 5 keer opnieuw
- Sla events op in een database voor auditing — zo mis je nooit een event
- Verwerk zware taken asynchroon (bijv. via een wachtrij) na de 200-respons
- Gebruik idempotente verwerking — hetzelfde event kan meerdere keren binnenkomen
Foutoplossing
- 401 Ongeldige handtekening — zorg dat je de raw body gebruikt voor HMAC-berekening, niet de geparseerde JSON
- Webhook niet ontvangen — controleer of je endpoint publiek bereikbaar is (niet
localhost) - Geen events — ga naar het dashboard en controleer of de webhook actief is en de juiste events geselecteerd zijn