Nilovon Connect
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

  1. Register a webhook URL in the dashboard or via the SDK
  2. Connect sends a POST request to your URL whenever a subscribed event fires
  3. Your server verifies the signature and processes the event

Event types

EventFired when
message.sentMessage handed off to the delivery provider
message.deliveredDelivery confirmed by the provider
message.failedDelivery attempt failed
message.bouncedRecipient address bounced
message.rejectedMessage rejected by the provider
webhook.testTest 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:

verify.ts
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

server.ts
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.

On this page