Events you can trust.
15 event types, all live today. Every payload is HMAC-SHA256-signed; verifying takes ten lines in any language. Exponential-backoff retry, and failed deliveries can be replayed from the dashboard.
Fifteen live events.
Every one of these is emitted today. Nothing here is a placeholder — and you can pull a generated sample payload for any of them from GET /api/webhooks/events/samples.
reply.receivedA reply landed and was classified. Fires for every channel — email, WhatsApp, SMS, inbound webhook — with the same shape.Liveemail.sentAn outbound email was accepted by the relay.Liveemail.openedA tracking pixel fired. Display only — Apple Mail pre-fetches images, so don't treat it as engagement.Liveemail.clickedA tracked link was followed.Liveemail.bouncedAn outbound email failed at the relay.Liveconversion.recordedA conversion was ingested via the Conversions API.Livesuppression.createdAn address became suppressed. Mirror these into your own do-not-send list.Livedomain.verifiedA sending domain finished verifying.Livedomain.failedA previously-verified sending domain stopped verifying.Livedomain.driftA verified domain's DNS records changed underneath it.Livemailbox.connectedA mailbox was connected for reply ingestion (Gmail / Microsoft / IMAP).Livewhatsapp.receivedAn inbound WhatsApp message arrived (also fires reply.received).Livesms.receivedAn inbound SMS arrived (also fires reply.received).Liveautomation.firedAn automation action ran.Livetest.pingSent when you click Send test in the dashboard.Live
What we POST to your endpoint.
One envelope shape across every event type. Headers carry routing metadata; body carries the data.
# What we POST to your endpoint
# X-Email-Digit-Version: 2026-07-30
# X-Email-Digit-Signature: sha256=abc...,t=1748459269
# X-Email-Digit-Event: reply.received
# X-Email-Digit-Mode: live
# X-Email-Digit-Event-Id: 00000000-0000-0000-0000-000000000000
# X-Email-Digit-Delivery-Id: 00000000-0000-0000-0000-000000000000
# X-Email-Digit-Attempt: 1
{
"version": "2026-07-30",
"id": "00000000-0000-0000-0000-000000000000",
"type": "reply.received",
"mode": "live",
"created_at": "2026-05-28T14:21:09Z",
"data": {
"reply_id": "00000000-0000-0000-0000-000000000000",
"channel": "email",
"from_email": "sample@example.com",
"from_name": "Sample Sender",
"phone": null,
"subject": "Re: your demo",
"party_type": "lead",
"category": "sales",
"intent": "interested",
"sentiment": "positive",
"urgency": "normal",
"intensity": 62,
"priority_score": 74,
"risk_flags": [],
"needs_review": false,
"confidence": 0.91,
"received_at": "2026-05-28T14:20:55+00:00",
"contact_id": "00000000-0000-0000-0000-000000000000"
}
}Ten lines, any language.
We sign sha256({timestamp}.body) using your subscription's signing secret. Reject anything older than 5 minutes.
import crypto from "crypto";
import express from "express";
const SECRET = process.env.ED_WEBHOOK_SECRET;
function verify(rawBody, header) {
const [sigPart, tsPart] = header.split(",");
const sig = sigPart.slice("sha256=".length);
const ts = tsPart.slice("t=".length);
// Reject anything older than 5 minutes (replay defense)
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = crypto.createHmac("sha256", SECRET)
.update(`${ts}.`).update(rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}
// IMPORTANT: use express.raw — JSON parsers break the MAC
app.post("/webhooks/email-digit",
express.raw({ type: "application/json" }),
(req, res) => {
if (!verify(req.body, req.get("X-Email-Digit-Signature"))) {
return res.status(401).send("invalid");
}
const event = JSON.parse(req.body.toString("utf8"));
// dispatch on event.type
res.status(200).send("ok");
});Exponential backoff, then dead.
2xx → done. 4xx (except 429) → permanent failure, no retry. Anything else → retry.
After 25 consecutive failures across all events the subscription auto-pauses so we don't hammer a permanently-broken endpoint. Resume it from the dashboard once you've fixed things. Failed deliveries can be replayed individually from /dashboard/webhooks.
Sign. Send. Retry.
All the way down.
Subscribe an endpoint in the dashboard, click Send test, watch the verification pattern work in your code in two minutes.