Skip to main content
Webhooks push email events to your backend in real time so you can react to deliveries, bounces, and engagement without polling the API. Use them to update user records, retry failed sends with a different address, or surface campaign stats in your own dashboard.

Events

EventFires when
deliveryThe receiving mail server accepted the message
bounceThe message bounced (hard or soft)
complaintThe recipient marked the message as spam
openThe recipient opened the message (tracking pixel loaded)
clickThe recipient clicked a tracked link
*All of the above (wildcard subscription)

Create a webhook

1

Open Webhooks

Go to Settings > Email > Webhooks > Add Webhook.
2

Set the URL

The HTTPS endpoint Hiveku will POST events to (e.g., https://api.acme.com/webhooks/hiveku).
3

Pick events

Subscribe to specific events or use * for all. You can subscribe to multiple.
4

Optional: tag filters

Restrict the webhook to fire only for emails carrying specific tags (e.g., marketing).
5

Copy the signing secret

Hiveku generates a signing secret per webhook — copy it for signature verification.

Event payload

All events share a common envelope:
{
  "id": "evt_01HPQRS...",
  "type": "delivery",
  "created_at": "2026-04-17T12:34:56Z",
  "data": {
    "email_id": "msg_01HPQRS...",
    "to": "customer@example.com",
    "from": "noreply@mail.acme.com",
    "subject": "Your order has shipped",
    "tags": ["order-shipped", "tenant-42"],
    "metadata": {
      "smtp_response": "250 OK",
      "mta": "mx1.example.com"
    }
  }
}
Event-specific data fields:
  • bouncebounce_type (hard | soft), bounce_subtype, diagnostic_code
  • complaintcomplaint_type (abuse, fraud, virus, other), feedback_id
  • openip_address, user_agent, timestamp
  • clickurl, ip_address, user_agent, timestamp

Verifying signatures

Every delivery includes an X-Hiveku-Signature header containing an HMAC-SHA256 of the raw body using your webhook secret. Always verify before trusting the payload.
import crypto from "node:crypto";
import express from "express";

const app = express();

app.post(
  "/webhooks/hiveku",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.header("X-Hiveku-Signature") ?? "";
    const expected = crypto
      .createHmac("sha256", process.env.HIVEKU_WEBHOOK_SECRET!)
      .update(req.body)
      .digest("hex");

    const valid = crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expected),
    );

    if (!valid) {
      return res.status(401).send("invalid signature");
    }

    const event = JSON.parse(req.body.toString("utf8"));
    // ... handle event.type ...

    res.status(200).send("ok");
  },
);
Verify against the raw request body — not a re-serialized JSON object. Any whitespace change will invalidate the signature.

Auto-disable on failures

Hiveku retries failed deliveries with exponential backoff (see Retry policy below). If a webhook returns non-2xx for every retry across 24 consecutive hours, it is automatically disabled and you get an email alert. Re-enable it from the dashboard after fixing the endpoint.

Health monitoring

The webhook detail page shows:
  • Last delivery attempt and status
  • Success rate over the past 24 hours / 7 days
  • Recent failures with full request and response
  • A Send Test Event button to confirm the endpoint is live

Tag-based filtering

If you only care about certain tags (say, marketing but not transactional), set a tag filter on the webhook. Hiveku only fires the webhook when the originating email carried at least one matching tag. Tag filters are AND-ed with the event-type subscription.

Retry policy

Non-2xx responses trigger retries with exponential backoff:
  • Attempt 1: immediate
  • Attempt 2: 30s later
  • Attempt 3: 2 min later
  • Attempt 4: 10 min later
  • Attempts 5-10: hourly
  • Subsequent attempts: every 6 hours for up to 24 hours
After 24 hours of failures the webhook is disabled. Hiveku treats any 2xx response as success — even an empty body is fine.
Acknowledge quickly (within a few seconds) and process asynchronously. Long-running work in the webhook handler itself can cause timeouts and unnecessary retries.