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

# UniBee webhooks

> Receive outbound billing events from UniBee at your HTTPS endpoint, verify deliveries, and acknowledge with a success response.

## What are UniBee webhooks?

UniBee sends **outbound** webhook events to merchant-configured HTTPS endpoints when billing state changes, for example:

* `payment.success`
* `invoice.created`
* `subscription.auto_renew.success`
* `subscription.activated`

These are **not** payment-gateway callbacks into UniBee. Stripe, PayPal, and similar **inbound** gateway webhooks are configured separately in payment gateway setup.

Use webhooks when your backend must react to billing events as soon as UniBee generates them. You can also inspect deliveries and resend failed attempts in the Admin Portal under **Configuration → Webhooks**.

***

## Configure an endpoint

### Admin Portal

1. Open **Configuration → Webhooks**.
2. Click **+** to add an endpoint.
3. Enter the webhook URL.
4. Select the events you want.
5. Confirm.

**Webhook URL requirements**

* Publicly reachable from the internet
* **HTTPS** only
* Accepts HTTP `POST` with JSON body

For local testing, use a staging server or an HTTPS tunnel such as [ngrok](https://ngrok.com/) or [localtunnel](https://localtunnel.github.io/www/). Keep the URL stable while testing.

### Merchant API

You can manage endpoints programmatically:

| Capability                         | API reference                                                                                                                                                          |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| List subscribable events           | [Webhook event list](/api-reference/webhook/webhook-event-list)                                                                                                        |
| Create / update / delete endpoints | [New](/api-reference/webhook/new-webhook-endpoint), [Update](/api-reference/webhook/update-webhook-endpoint), [Delete](/api-reference/webhook/delete-webhook-endpoint) |
| List endpoints                     | [Get webhook endpoint list](/api-reference/webhook/get-webhook-endpoint-list)                                                                                          |
| Get webhook secret                 | [Get webhook secret](/api-reference/webhook/get-webhook-secret)                                                                                                        |
| View delivery logs                 | [Get webhook endpoint log list](/api-reference/webhook/get-webhook-endpoint-log-list)                                                                                  |
| Resend a delivery                  | [Resent webhook](/api-reference/webhook/resent-webhook)                                                                                                                |

Subscribe only to events returned by the event list. The full catalog and trigger notes are in [Webhook events](/documentation/webhooks/webhook-events).

In non-production environments, each merchant can configure up to **16** sandbox endpoints.

***

## Delivery format

### HTTP request

* Method: `POST`
* `Content-Type`: `application/json`
* Body: business payload with these top-level fields injected before send:
  * `eventType`
  * `eventId`
  * `msgId`
  * `datetime` (`2006-01-02T15:04:05+08:00`)

### Request headers

| Header                  | Description                                                                                               |
| ----------------------- | --------------------------------------------------------------------------------------------------------- |
| `Content-Type`          | `application/json`                                                                                        |
| `Authorization`         | `Bearer {merchant.ApiKey}`                                                                                |
| `EventType`             | Event name                                                                                                |
| `EventId`               | Same as body `eventId`                                                                                    |
| `Msg-id`                | Same as body `msgId`                                                                                      |
| `Datetime`              | Same as body `datetime`                                                                                   |
| `X-Signature-Algorithm` | `hmac`                                                                                                    |
| `X-Signature`           | HMAC-SHA256 of the **raw JSON body string**, Base64-encoded, using the merchant **API key** as the secret |

Use `eventId` or `msgId` for idempotency. Retries for the same business action may use new IDs.

***

## Verify webhook requests

UniBee signs each delivery with **HMAC-SHA256** over the full JSON body string. Verify `X-Signature` with your merchant **API key** before processing the payload.

`GET /get_webhook_secret` can return or generate a webhook secret for endpoint management workflows. Current outbound webhook signatures use the merchant API key as the HMAC secret.

You can also check that `Authorization` matches `Bearer {API_KEY}` as an additional guard.

```javascript theme={null}
const crypto = require('crypto');

app.post('/webhooks/unibee', express.raw({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['x-signature'];
  const algorithm = req.headers['x-signature-algorithm'];
  const eventType = req.headers['eventtype'];
  const rawBody = req.body.toString();

  if (!signature || algorithm !== 'hmac') {
    return res.status(401).send('Missing signature');
  }

  const expectedSignature = crypto
    .createHmac('sha256', process.env.UNIBEE_API_KEY)
    .update(rawBody)
    .digest('base64');

  const signatureBuffer = Buffer.from(signature);
  const expectedSignatureBuffer = Buffer.from(expectedSignature);

  if (
    signatureBuffer.length !== expectedSignatureBuffer.length ||
    !crypto.timingSafeEqual(signatureBuffer, expectedSignatureBuffer)
  ) {
    return res.status(401).send('Invalid signature');
  }

  const payload = JSON.parse(rawBody);

  switch (payload.eventType || eventType) {
    case 'subscription.activated':
      await handleSubscriptionActivated(payload);
      break;
    case 'invoice.paid':
      await handleInvoicePaid(payload);
      break;
    default:
      break;
  }

  res.status(200).send('success');
});
```

**Verification notes**

* Verify the **raw body string** before JSON parsing.
* Compare signatures with `crypto.timingSafeEqual()` or an equivalent constant-time compare.
* See [Webhook events](/documentation/webhooks/webhook-events) for the event catalog and payload samples.

***

## Acknowledgement, retries, and idempotency

Your endpoint must return HTTP **200** with a response body of exactly `success` after trimming whitespace. Any other body, including other 2xx responses, is treated as failure and queued for retry.

UniBee retries failed deliveries up to about **8** attempts. Intervals usually increase from about **1 minute** to about **7 minutes**.

Process events idempotently with `eventId` or `msgId`. Do not reuse the same idempotency key for recurring tracking events such as `subscription.track*`.

***

## Webhook logs

1. Open **Configuration → Webhooks**.
2. Select an endpoint to view deliveries.
3. Inspect request body and response.
4. Use **Resend** to replay a delivery when needed.

Each log includes timestamp, URL, `Msg-id`, event name, response, retry count, and the JSON payload.

***

## Testing

1. Point integrations at the sandbox API (`https://api-sandbox.unibee.top`).
2. Configure a public HTTPS webhook URL.
3. Trigger billing flows and confirm deliveries in the Admin Portal.
4. Verify signature checks and idempotency handling.

Before production:

* [ ] HTTPS endpoint
* [ ] HMAC verification implemented
* [ ] Idempotency on `eventId` / `msgId`
* [ ] Response body is exactly `success`
* [ ] Monitoring for failed deliveries
* [ ] Subscribed events tested end to end

For payment-related tests, see [Testing cards](/documentation/business-integration/testing-cards).
