Webhooks
Real-time delivery events delivered to an HTTPS endpoint you control. Every payload is signed with HMAC-SHA256 so you can prove it came from us.
Events
| Event | When it fires |
|---|---|
message.sent | SMTP accepted the message |
message.delivered | Recipient mailbox accepted final delivery |
message.bounced | Hard bounce — recipient auto-suppressed |
message.opened | Tracking pixel hit (per recipient) |
message.clicked | Tracked link clicked |
message.unsubscribed | Recipient hit the one-click unsubscribe link |
message.complained | Mailbox provider reported a spam complaint |
Add an endpoint
Settings → Webhooks → New endpoint. Pick the events you want, paste the URL, copy the signing secret (we show it once).
Payload format
{
"event": "message.sent",
"timestamp": 1715764800,
"message_id": "8f30b3...",
"to": "[email protected]",
"from": "[email protected]",
"subject": "Hello"
}Verify the signature
Every request includes two headers:
| Header | Example |
|---|---|
X-Koltrix-Event | message.sent |
X-Koltrix-Signature | sha256=abc123... |
import crypto from "node:crypto";
function verify(rawBody: string, signature: string, secret: string) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}import hmac, hashlib
def verify(body: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)Retries (coming soon)
Failed deliveries are currently logged once. Persistent retry is on the
roadmap — until then, your endpoint should respond within 8 seconds with
2xx, and we suggest you 200 immediately and process async.
Testing
Hit Send test next to a webhook in the dashboard to receive a synthetic
message.sent event for debugging.