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

# Webhooks

> Receive real-time event notifications for customer, bank, payin, payout, and EDD state changes.

Webhooks push real-time updates to your server when objects in DollarPe change state. Use them instead of polling. They are the recommended way to drive your integration logic.

<Note>
  To configure your webhook URL, see the [Webhook Configuration](/api-reference-exchange/endpoint/organizations/api-webhooks) API reference.
</Note>

## How it works

When an event fires, DollarPe sends an HTTP POST to your configured endpoint. Your server must respond with a `2xx` status code to acknowledge receipt. Non-2xx responses are treated as delivery failures.

## Webhook structure

All events share the same envelope:

```json theme={null}
{
  "type": "string",       // Entity type: PAYIN, PAYOUT, CUSTOMER, BANK, EDD
  "id": "string",         // Unique ID of the entity
  "event": "string",      // The state the entity moved to
  "timestamp": "string",  // ISO 8601 timestamp
  "metadata": {
    "failure_reason": "string"   // Present on FAILED events
  }
}
```

Use `id` to deduplicate events. Webhooks may occasionally be delivered more than once, so your handler should be idempotent.

## Signature validation

Every request includes two headers:

```
X-TIMESTAMP: 1752670745
X-SIGNATURE: ohmVyfuNup6eKWOdOZBBZut5CMPdPTfFSB/zDT/eXQo=
```

* `X-TIMESTAMP` — Unix timestamp when the webhook was generated
* `X-SIGNATURE` — HMAC-SHA256 of the request, Base64 encoded

Validate both on every incoming request. Reject anything with a stale timestamp or a signature mismatch.

```python theme={null}
import base64
import hmac
import hashlib
import json

def generate_hmac_signature(api_key, api_secret, timestamp, body={}):
    body = json.dumps(body, sort_keys=True, separators=(",", ":"))
    message = f"{api_key}|{timestamp}|{body}".encode()
    signature = hmac.new(api_secret.encode(), message, hashlib.sha256).digest()
    return base64.b64encode(signature).decode()

def verify_hmac_signature(api_key, timestamp, signature, body, api_secret):
    expected = generate_hmac_signature(api_key, api_secret, timestamp, body)
    return hmac.compare_digest(expected, signature)
```

**Usage:**

```python theme={null}
is_valid = verify_hmac_signature(
    api_key="your_api_key",
    api_secret="your_api_secret",
    timestamp=received_timestamp,
    signature=received_signature,
    body=webhook_body_dict,
)

if not is_valid:
    return 401  # reject
```

<Warning>
  Always validate the signature before processing any webhook payload. Never trust the payload without verification.
</Warning>

## Metadata fields

The `metadata` object carries event-specific context.

### Payin events

* `failure_reason` — present on `FAILED`. Values: `INCORRECT_UTR`, `PAYMENT_NOT_RECEIVED`, and others. See [Failure Reason Reference](/api-reference-exchange/appendix/failure-reason-reference)
* `refund_reason` — present on `REFUND_INITIATED` and `REFUNDED`. Values: `INCORRECT_AMOUNT`, `PAYMENT_FROM_NON_WHITELISTED_ACCOUNT`, `THIRD_PARTY_PAYMENT`

### Payout events

* `utr` — present on `SUCCESS`. The UTR number for the bank transfer, usable as a reconciliation reference

### Customer events

* `failure_reason` — present on `FAILED`. Values: `DOCUMENT_VERIFICATION_FAILED`, `TAX_VERIFICATION_FAILED`, `KYC_FAILED`

### Bank events

* `failure_reason` — present on `FAILED`. Values: `ACCOUNT_TYPE_NRE`, `PENNY_DROP_FAILED`, `NAME_MISMATCH`, `BANK_RISK_CHECK_FAILED`

Metadata fields are only present when relevant. Successful events typically have an empty `metadata` object.

***

## Event catalog

### Customer events

Fired when a customer's KYC status changes.

| Event      | Description                                      |
| ---------- | ------------------------------------------------ |
| `VERIFIED` | KYC passed. Customer can link banks and transact |
| `FAILED`   | KYC failed or account suspended                  |

```json theme={null}
{
  "type": "CUSTOMER",
  "event": "FAILED",
  "id": "12348400-e29b-41d4-a716-446655440000",
  "timestamp": "2024-03-13T10:00:00Z",
  "metadata": {
    "failure_reason": "TAX_VERIFICATION_FAILED"
  }
}
```

### Bank events

Fired when a bank account or UPI ID verification resolves.

| Event      | Description                             |
| ---------- | --------------------------------------- |
| `VERIFIED` | Bank account or UPI ID ready for orders |
| `FAILED`   | Verification failed                     |

```json theme={null}
{
  "type": "BANK",
  "event": "VERIFIED",
  "id": "12348400-e29b-41d4-a716-446655440000",
  "timestamp": "2024-03-13T10:00:00Z",
  "metadata": {}
}
```

### Payin events

Fired when a payin order changes state.

| Event              | Description                         |
| ------------------ | ----------------------------------- |
| `SUCCESS`          | Payment received, crypto released   |
| `FAILED`           | Payin failed                        |
| `REFUND_INITIATED` | Refund process started              |
| `REFUNDED`         | Fiat returned to source             |
| `ON_HOLD`          | Payment flagged and held for review |

```json theme={null}
{
  "type": "PAYIN",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "event": "SUCCESS",
  "timestamp": "2024-03-13T10:00:00Z",
  "metadata": {}
}
```

```json theme={null}
{
  "type": "PAYIN",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "event": "FAILED",
  "timestamp": "2024-03-13T10:00:00Z",
  "metadata": {
    "failure_reason": "INCORRECT_UTR"
  }
}
```

```json theme={null}
{
  "type": "PAYIN",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "event": "REFUND_INITIATED",
  "timestamp": "2024-03-13T10:00:00Z",
  "metadata": {
    "refund_reason": "INCORRECT_AMOUNT"
  }
}
```

### Payout events

Fired when a payout order changes state.

| Event       | Description                    |
| ----------- | ------------------------------ |
| `SUCCESS`   | Funds delivered to beneficiary |
| `FAILED`    | Payout failed                  |
| `REFUNDED`  | Payout amount returned         |
| `CANCELLED` | Payout cancelled               |

```json theme={null}
{
  "type": "PAYOUT",
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "event": "SUCCESS",
  "timestamp": "2024-03-13T10:00:00Z",
  "metadata": {
    "utr": "RATN12341234XYZ"
  }
}
```

### EDD events

Fired when Enhanced Due Diligence verification resolves.

| Event      | Description                                     |
| ---------- | ----------------------------------------------- |
| `VERIFIED` | EDD passed. Customer gets enhanced payin limits |
| `FAILED`   | EDD failed. Standard limits remain              |

```json theme={null}
{
  "type": "EDD",
  "event": "VERIFIED",
  "id": "12348400-e29b-41d4-a716-446655440000",
  "timestamp": "2024-03-13T10:00:00Z",
  "metadata": {}
}
```
