> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hiveku.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive real-time email events via webhook

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

| Event       | Fires when                                               |
| ----------- | -------------------------------------------------------- |
| `delivery`  | The receiving mail server accepted the message           |
| `bounce`    | The message bounced (hard or soft)                       |
| `complaint` | The recipient marked the message as spam                 |
| `open`      | The recipient opened the message (tracking pixel loaded) |
| `click`     | The recipient clicked a tracked link                     |
| `*`         | All of the above (wildcard subscription)                 |

## Create a webhook

<Steps>
  <Step title="Open Webhooks">
    Go to **Settings > Email > Webhooks > Add Webhook**.
  </Step>

  <Step title="Set the URL">
    The HTTPS endpoint Hiveku will POST events to (e.g., `https://api.acme.com/webhooks/hiveku`).
  </Step>

  <Step title="Pick events">
    Subscribe to specific events or use `*` for all. You can subscribe to multiple.
  </Step>

  <Step title="Optional: tag filters">
    Restrict the webhook to fire only for emails carrying specific tags (e.g., `marketing`).
  </Step>

  <Step title="Copy the signing secret">
    Hiveku generates a signing secret per webhook — copy it for [signature verification](#verifying-signatures).
  </Step>
</Steps>

## Event payload

All events share a common envelope:

```json theme={null}
{
  "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:

* **`bounce`** — `bounce_type` (`hard` | `soft`), `bounce_subtype`, `diagnostic_code`
* **`complaint`** — `complaint_type` (`abuse`, `fraud`, `virus`, `other`), `feedback_id`
* **`open`** — `ip_address`, `user_agent`, `timestamp`
* **`click`** — `url`, `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.

```ts theme={null}
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");
  },
);
```

<Warning>
  Verify against the **raw request body** — not a re-serialized JSON object. Any whitespace change will invalidate the signature.
</Warning>

## Auto-disable on failures

Hiveku retries failed deliveries with exponential backoff (see [Retry policy](#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.

<Tip>
  Acknowledge quickly (within a few seconds) and process asynchronously. Long-running work in the webhook handler itself can cause timeouts and unnecessary retries.
</Tip>
