> For the complete documentation index, see [llms.txt](https://docs.esimpay.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.esimpay.net/eng/webhook/webhook-notification-handling.md).

# Webhook Notification Handling

eSIMPAY sends webhooks via POST with a JSON body.

&#x20;Specify your server's URL in eSIMPAY. Only HTTPS addresses are allowed.

{% hint style="info" %}
Requests include the `CF-ACCESS-SIGN`. Verify the signature before processing (see the “Signature verification” section).
{% endhint %}

#### Notification types

| Type               | When it's sent                                                                                                                                          |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| eSIM Status        | After the eSIM profile is installed on the device                                                                                                       |
| Balance Activation | When the data balance is activated (especially for FIRST\_USE / ON\_DEMAND)                                                                             |
| Low Balance Alert  | When the remaining data drops below 100 MB                                                                                                              |
| Order Status       | Notification of order status (body matches a successful order-status response). The URL can also be passed in the callback field when creating an order |

#### Signature verification

The signature in the CF-ACCESS-SIGN header is Base64 of HMAC-SHA256(api\_secret, raw\_body), where raw\_body is the raw request body (bytes) before JSON parsing.

**api\_secret** — your API SECRET, issued when you connected.

```python
import hmac, hashlib, base64

def verify_webhook(raw_body: bytes, signature_header: str, api_secret: str) -> bool:
    """
    raw_body:         The raw request body (bytes), BEFORE parsing the JSON.
    signature_header: heading CF-ACCESS-SIGN.
    api_secret:       Your API SECRET, issued upon connection.
    """
    if not signature_header:
        return False
    digest = hmac.new(api_secret.encode('utf-8'), raw_body, hashlib.sha256).digest()
    expected = base64.b64encode(digest).decode('utf-8')
    return hmac.compare_digest(expected, signature_header)
```
