Guides
Webhooks
Real-time delivery notifications for your application.
Webhooks notify your application in real time when message events occur — delivery confirmations, failures, bounces, and more.
How it works
- Register a webhook URL in the dashboard or via the SDK
- Connect sends a POST request to your URL whenever a subscribed event fires
- Your server verifies the signature and processes the event
Event types
| Event | Fired when |
|---|---|
message.sent | Message handed off to the delivery provider |
message.delivered | Delivery confirmed by the provider |
message.failed | Delivery attempt failed |
message.bounced | Recipient address bounced |
message.rejected | Message rejected by the provider |
webhook.test | Test event triggered manually |
Payload format
Every webhook POST includes a JSON body:
{
"type": "message.delivered",
"timestamp": "2025-01-15T10:30:00.000Z",
"data": {
"id": "msg_...",
"channelType": "email",
"to": "user@example.com",
"status": "delivered"
}
}Signature verification
Every request includes an X-Webhook-Signature header. Always verify it before processing:
import crypto from "node:crypto";
function verify(body: string, signature: string, secret: string): boolean {
const expected = `sha256=${crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex")}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}Express example
import express from "express";
const app = express();
app.post("/webhooks/connect", express.raw({ type: "application/json" }), (req, res) => {
const body = req.body.toString();
const signature = req.headers["x-webhook-signature"] as string;
if (!verify(body, signature, process.env.WEBHOOK_SECRET!)) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(body);
console.log(event.type, event.data.id);
res.status(200).send("OK");
});Event log
Every webhook keeps a delivery log. Use it to debug failed deliveries:
const { items } = await connect.webhook.listEvents({
webhookId: "wh_...",
status: "failed",
limit: 20,
offset: 0,
});Each event entry includes the HTTP responseStatus, responseBody, and number of delivery attempts.