> For the complete documentation index, see [llms.txt](https://docs.ledig.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ledig.io/hooks/webhooks.md).

# Webhooks

Ledig uses webhooks to notify your system in near‑real time when key events occur in your account.\
When an event is triggered, Ledig sends an HTTPS `POST` request with a JSON body to your registered webhook URL.

***

## Configure your webhook URL

You can configure your webhook URL in the Ledig Dashboard under **Settings → API & Webhooks**.\
If you do not see this section, contact us through **Request an account** on the homepage and we’ll follow up within 24 hours.

**Requirements**

* Your endpoint **must be HTTPS** and publicly reachable.
* Respond **quickly** and do heavier processing asynchronously.
* If you rotate API keys, remember to **update any downstream services** that depend on webhook data.

***

## Delivery behavior

* **Method**: `POST`
* **Content-Type**: `application/json`
* **Retries**: If your server responds with a non‑2xx code, Ledig retries the request **up to 3 times**.
* **Acknowledgement**: Always return **HTTP 200 OK**. You may optionally return `{ "ok": true }` in the response body.

> Tip: Because retries can produce duplicates, write your handler to be **idempotent**. Use the event `id` when present, or a dedup hash of the payload.

***

## Example delivery

```
POST /your/webhook/endpoint HTTP/1.1
Host: client.com
Content-Type: application/json
```

```json
{
  "event": "crypto_received",
  "id": "57L94BW6EOAGC58X",
  "wallet_alias": "proper Vault",
  "wallet_address": "0x20f4065BA127a801Ef20a4D7a1C94e61896857a0",
  "tx_hash": "0x496a74033fb7e489044297eb84594292ec781a4f19ca5ca091b343527514bde1",
  "status": "successful",
  "Business": "Facebook(test)",
  "coin": "USDC",
  "Amount": "0.5",
  "fee": "0"
}
```

**Expected response from your server**

```json
{ "ok": true }
```

***

## Event summary

| Event                       | When it fires                                            | Key fields                                                                                                                    |
| --------------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `settlement_wallet_update`  | A settlement wallet you submitted is reviewed or updated | `Alias`, `Address`, `Network`, `Asset Supported`, `status`, `Business`                                                        |
| `settlement_account_update` | A fiat settlement account is reviewed or updated         | `Alias`, `Bank Name`, `Account Number`, `Account Name`, `status`, `Currency`, `other details`                                 |
| `crypto_received`           | On‑chain funds are detected on one of your wallets       | `id`, `wallet_alias`, `wallet_address`, `tx_hash`, `coin`, `Amount`, `status`, `fee`                                          |
| `virtual_account_created`   | A new fiat virtual account is issued to you              | `Alias`, `Bank`, `Account Number`, `Account Name`, `Merchant Associated`, `Business`, `Currency`                              |
| `virtual_account_credited`  | A deposit lands in one of your virtual accounts          | `id`, `currency`, `Amount`, `status`, `Account received alias`, `Sender Bank`, `Sender account name`, `Sender account number` |
| `crypto_payout_update`      | The status of a crypto payout changes                    | `id`, `coin`, `Amount sent`, `Wallet received alias`, `status`, `fee`, `tx_hash`                                              |
| `admin_funding`             | An admin credits or debits your balance                  | `id`, `Asset`, `Amount`, `status`, `Business`, `fee`, `credit/debit`, `fiat/crypto`                                           |

***

## Event payloads

### 1. `settlement_wallet_update`

```json
{
  "event": "settlement_wallet_update",
  "Alias": "testing vaulty",
  "Address": "0x20f4065BA127a801Ef20a4D7a1C94e61896857a0",
  "Network": "Ethereum Erc20",
  "Asset Supported": "USDT, USDC",
  "status": "Rejected",
  "Business": "Facebook(test)"
}
```

**Use it to**

* Track the review process and final status for wallets submitted via **Add Settlement Wallet**.
* Update your allow‑list of payout destinations once a wallet becomes **Whitelisted**.

***

### 2. `settlement_account_update`

```json
{
  "event": "settlement_account_update",
  "Alias": "qasq",
  "Bank Name": "qa",
  "Account Number": "0129293239339",
  "Account Name": "aaqaq",
  "status": "Whitelisted",
  "Business": "Facebook(test)",
  "other details": "ssaaa\nggjntg\ngtrgkngthgkntknh\ntrmglmlthg",
  "Currency": "KES"
}
```

**Use it to**

* Detect when fiat settlement accounts become **Whitelisted** and safe for payouts.
* Refresh banking details when admins make changes.

***

### 3. `crypto_received`

```json
{
  "event": "crypto_received",
  "id": "57L94BW6EOAGC58X",
  "wallet_alias": "proper Vault",
  "wallet_address": "0x20f4065BA127a801Ef20a4D7a1C94e61896857a0",
  "tx_hash": "0x496a74033fb7e489044297eb84594292ec781a4f19ca5ca091b343527514bde1",
  "status": "successful",
  "Business": "Facebook(test)",
  "coin": "USDC",
  "Amount": "0.5",
  "fee": "0"
}
```

**Use it to**

* Credit customer balances when on‑chain deposits arrive.
* Update transaction history with `tx_hash` and final `status`.

***

### 4. `virtual_account_created`

```json
{
  "event": "virtual_account_created",
  "Alias": "Treasury account",
  "Bank": "Ledig bank",
  "Account Number": "001726334",
  "Account Name": "Konoha LTD",
  "Merchant Associated": "Uchiha LTD",
  "Business": "Konoha LTD",
  "Currency": "NGN"
}
```

**Use it to**

* Store the newly issued virtual account details for invoicing or internal routing.

***

### 5. `virtual_account_credited`

```json
{
  "event": "virtual_account_credited",
  "id": "dfeff3fcdd31dsdd",
  "currency": "NGN",
  "status": "successful",
  "Amount": "30000000",
  "Account received alias": "Vault B",
  "Business": "Konoha LTD",
  "Merchant Associated": "Uchiha Ltd",
  "Sender Bank": "Yaegar Bank",
  "Sender account name": "Paradis LTD",
  "Sender account number": "002912283"
}
```

**Use it to**

* Credit the correct sub‑ledger when a bank transfer lands in your virtual account.
* Reconcile sender details with your payers list.

***

### 6. `crypto_payout_update`

```json
{
  "event": "crypto_payout_update",
  "id": "dfeff3fcdd31dsdd",
  "coin": "usdt",
  "status": "successful",
  "Amount sent": "30000000",
  "Wallet received alias": "receiver A",
  "Business": "Konoha LTD",
  "fee": "20 usdt",
  "tx_hash": "gfgfhgghgjtyhjtyjjjkjmu"
}
```

**Use it to**

* Update payout status in your UI and post the blockchain `tx_hash` for tracking.
* Trigger notifications to finance or counterparties when payouts settle.

***

### 7. `admin_funding`

```json
{
  "event": "admin_funding",
  "id": "dfeff3fcdd31dsdd",
  "Asset": "NGN",
  "status": "successful",
  "Amount": "30000000",
  "Business": "Konoha LTD",
  "fee": "0",
  "credit/debit": "Credit",
  "fiat/crypto": "Fiat"
}
```

**Use it to**

* Reflect manual adjustments performed by Ledig admins in your internal balances.

***

## Building your webhook handler

Below are minimal examples. They simply parse JSON, log the event, and return `{ "ok": true }` quickly. Do heavier processing asynchronously.

### Node.js (Express)

```js
import express from "express";

const app = express();
app.use(express.json({ type: "application/json" }));

app.post("/webhooks/ledig", async (req, res) => {
  const evt = req.body;

  // TODO: implement idempotency (evt.id when present)
  console.log("Ledig webhook:", evt);

  // Acknowledge quickly
  return res.status(200).json({ ok: true });
});

app.listen(3000, () => console.log("Webhook server listening on 3000"));
```

### Python (Flask)

```python
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.post("/webhooks/ledig")
def ledig_webhook():
    evt = request.get_json(force=True, silent=True) or {}
    # TODO: idempotency using evt.get("id")
    print("Ledig webhook:", evt)
    return jsonify({"ok": True}), 200

if __name__ == "__main__":
    app.run(port=3000)
```

### cURL test

```bash
curl -X POST https://your-domain.com/webhooks/ledig   -H "Content-Type: application/json"   -d '{
    "event":"crypto_received",
    "id":"TEST123",
    "coin":"USDT",
    "Amount":"10",
    "status":"successful"
  }'
```

***

## Best practices

* **Acknowledge fast**. Do not block on long‑running work.
* **Idempotency**. Use `id` when present, or hash the payload.
* **Queue processing**. Publish to a queue or job worker, then return 200.
* **Monitor failures**. Alert on repeated non‑2xx responses from your endpoint.
* **Keep your URL updated**. Rotate or migrate endpoints via the dashboard settings.

***

## Troubleshooting

* You are not receiving events
  * Confirm your URL is publicly reachable over HTTPS.
  * Check that the URL is correctly configured in **Settings → API & Webhooks**.
  * Inspect your server logs for non‑2xx status codes and timeouts.
* You see duplicate deliveries
  * Implement idempotency as described above. Retries can cause duplicates.
* Payload fields differ from your expectations
  * Some attributes may change as Ledig evolves. Write tolerant parsers and log unknown fields for visibility.
