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

# Webhooks

> Receive an event whenever an order changes status, and verify its signature.

Instead of polling, register an HTTPS endpoint (public host, port 443). We POST an event whenever an order changes status. A webhook URL must be a public `https` URL on port 443 with a hostname (no IP addresses, no credentials in the URL).

| # | Method | URL | What it does |
| - | - | - | - |
| 6 | `POST` | [`/webhook`](https://docs.balaipedia.com/api-reference/set-webhook) | Set your webhook URL (or rotate its secret). |
| 7 | `GET` | [`/webhook`](https://docs.balaipedia.com/api-reference/get-webhook) | Read the current webhook setting. |
| 8 | `POST` | [`/webhook/disable`](https://docs.balaipedia.com/api-reference/disable-webhook) | Remove your webhook. |
| 9 | `POST` | [`/webhook/test`](https://docs.balaipedia.com/api-reference/test-webhook) | Send a test event to your webhook. |

## Events and payload

Events: `order.pending`, `order.success`, `order.error` (one per status change, named after the new status). The `data` object is the [order](https://docs.balaipedia.com/api-reference/get-order.md#the-order-object), including `amount` and `currency`.

```http theme={null}
POST https://example.com/hooks/orders
X-BP-Event: order.success
X-BP-Delivery: evt_5d1c0e7a2b9f4c33
X-BP-Timestamp: 1789000000
X-BP-Signature: v1=3f2a...

{"id":"evt_5d1c0e7a2b9f4c33","type":"order.success","created_at":"...","data":{ ...order object... }}
```

## Verify signatures

Verify every request: compute `HMAC-SHA256(secret, timestamp + "." + rawBody)` as lowercase hex, compare it to the value after `v1=` in constant time, and reject timestamps older than 5 minutes. Use the **raw** request body.

```javascript Node.js theme={null}
const crypto = require("crypto");
function verify(rawBody, headers, secret) {
  const ts = headers["x-bp-timestamp"];
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const mac = crypto.createHmac("sha256", secret).update(ts + "." + rawBody).digest("hex");
  const got = String(headers["x-bp-signature"] || "").replace("v1=", "");
  return got.length === mac.length && crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(got));
}
```

```php PHP theme={null}
<?php
$raw = file_get_contents("php://input");
$ts  = $_SERVER["HTTP_X_BP_TIMESTAMP"] ?? "";
$sig = str_replace("v1=", "", $_SERVER["HTTP_X_BP_SIGNATURE"] ?? "");
$mac = hash_hmac("sha256", $ts . "." . $raw, $secret);
if (abs(time() - (int)$ts) > 300 || !hash_equals($mac, $sig)) { http_response_code(401); exit; }
```

```python Python theme={null}
import hmac, hashlib, time
def verify(raw: bytes, ts: str, sig: str, secret: str) -> bool:
    if abs(time.time() - int(ts)) > 300: return False
    mac = hmac.new(secret.encode(), ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(mac, sig.replace("v1=", ""))
```

## Delivery and retries

Respond with any `2xx` within 8 seconds. Otherwise we retry with increasing delays (up to 6 attempts over roughly a day). Retries reuse the same event `id`, so deduplicate on it. Delivery order is not guaranteed; treat the `status` in the payload as the current state, or fetch the order to confirm.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.