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

# Fiat Webhooks

> Fiat Module webhooks for integrators: event types, payload structure, signature verification, and examples.

# Fiat Webhooks

> **Note:** all numeric values, amounts, and identifiers in the examples are illustrative and provided for demonstration purposes only.

## Overview

Fiat Module uses Crypto-Cash webhooks, which are sent to the merchant after a transaction status changes. They work only if the `webhookUrl` parameter is configured on the Fiat API key. If `webhookUrl` is not set, webhooks are not sent.

> The notification URL of the payment provider and the `webhookUrl` on the Fiat API key are different things. The `webhookUrl` on the Fiat API key is the address to which Crypto-Cash sends its events to the merchant.

## Key parameters

| Parameter                  | Value                                       |
| -------------------------- | ------------------------------------------- |
| Crypto-Cash webhook target | `webhookUrl` configured on the Fiat API key |
| HTTP method                | `POST`                                      |
| Delivery                   | Asynchronous                                |
| Data format                | `application/json`                          |

## How webhooks work

```
User opens the payment widget
↓
Payment provider processes the payment
↓
Payment provider sends a webhook to Crypto-Cash
↓
Crypto-Cash verifies the provider signature
↓
Crypto-Cash updates the transaction status
↓
Crypto-Cash creates a payment event
↓
If webhookUrl is configured on the Fiat API key,
Crypto-Cash sends the webhook asynchronously
```

## Crypto-Cash webhooks

Crypto-Cash webhooks are sent after payment events. They work only if `webhookUrl` is configured on the Fiat API key.

When a transaction status changes, the system creates a payment event and asynchronously sends a webhook to the configured `webhookUrl`. If `webhookUrl` is not configured, the webhook is not sent.

HTTP format:

```
Method: POST
Content-Type: application/json
```

Each webhook contains a `signature` field in the payload body. The merchant uses the Fiat API key to verify that the webhook was sent by Crypto-Cash and has not been modified.

The webhook payload and signature are stored for audit and history.

## Event types

| Event               | When it is sent              | Type         |
| ------------------- | ---------------------------- | ------------ |
| `payment::created`  | Transaction created          | Intermediate |
| `payment::waiting`  | Status changed to `Waiting`  | Intermediate |
| `payment::paid`     | Status changed to `Paid`     | **Final**    |
| `payment::failed`   | Status changed to `Fail`     | **Final**    |
| `payment::canceled` | Status changed to `Canceled` | **Final**    |

**Intermediate events** (`payment::created`, `payment::waiting`) mean that the transaction is in progress and further status changes are possible.

**Final events** (`payment::paid`, `payment::failed`, `payment::canceled`) mean that the transaction has reached a terminal state:

| Final event         | Meaning                                                    |
| ------------------- | ---------------------------------------------------------- |
| `payment::paid`     | Cryptocurrency was successfully sent to the wallet address |
| `payment::failed`   | The payment was not completed                              |
| `payment::canceled` | The payment was canceled                                   |

## What the merchant should check

When receiving a Crypto-Cash webhook:

1. Verify the `signature` field using the Fiat API key (see the “Webhook signature” section).
2. Read `event.event_type` to determine the event.
3. Read `event.data` — it contains the full transaction state.
4. Process the event depending on whether it is intermediate or final.

> The `id` field is unique for each webhook delivery. Use it for deduplication on your side.

## Crypto-Cash webhook signature

Each Crypto-Cash webhook contains a `signature` field in the payload body.

The signature is generated from the webhook identifier and content:

```
payload = { id, delivered_at, event }
```

The merchant verifies the signature as follows: reconstruct the same payload from the received webhook, **excluding the `signature` field**, and verify it using the Fiat API key.

The verification method depends on the Fiat API key type:

* `ED25519` — verify the received `signature` using the Fiat API `publicKey`.
* `LEGACY` — reconstruct the expected signature value using the Fiat API `privateKey` and compare it with the received one.

Use the same Fiat API key pair that is used for the merchant’s signed API requests. The private key must remain on the merchant side — do not send it in webhook responses, logs, or support requests.

## Payload examples

### `payment::created`

```json theme={null}
{
  "id": "webhook-id",
  "delivered_at": "2026-04-16T10:00:01.000Z",
  "event": {
    "event_type": "payment::created",
    "timestamp": "2026-04-16T10:00:01.000Z",
    "data": {
      "id": "crypto-cash-transaction-id",
      "externalId": "merchant-order-id",
      "status": "New",
      "fiatCurrency": "SGD",
      "fiatAmount": "51",
      "currency": "USDT",
      "network": "TRC20",
      "address": "wallet-address",
      "cryptoAmount": null,
      "exchangeRate": null,
      "paymentMethod": null,
      "hash": null,
      "buyId": null,
      "withdrawId": null,
      "customerId": null,
      "merchantEarning": null,
      "merchantId": "merchant-id",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:00:00.000Z"
    }
  },
  "signature": "signature"
}
```

### `payment::waiting`

```json theme={null}
{
  "id": "webhook-id",
  "delivered_at": "2026-04-16T10:00:15.000Z",
  "event": {
    "event_type": "payment::waiting",
    "timestamp": "2026-04-16T10:00:15.000Z",
    "data": {
      "id": "crypto-cash-transaction-id",
      "externalId": "merchant-order-id",
      "status": "Waiting",
      "fiatCurrency": "SGD",
      "fiatAmount": "51",
      "currency": "USDT",
      "network": "TRC20",
      "address": "wallet-address",
      "cryptoAmount": null,
      "exchangeRate": null,
      "paymentMethod": "card",
      "hash": null,
      "buyId": "provider-operation-id",
      "withdrawId": null,
      "customerId": "customer-id",
      "merchantEarning": null,
      "merchantId": "merchant-id",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:00:10.000Z"
    }
  },
  "signature": "signature"
}
```

### `payment::paid`

```json theme={null}
{
  "id": "webhook-id",
  "delivered_at": "2026-04-16T10:07:30.000Z",
  "event": {
    "event_type": "payment::paid",
    "timestamp": "2026-04-16T10:07:30.000Z",
    "data": {
      "id": "crypto-cash-transaction-id",
      "externalId": "merchant-order-id",
      "status": "Paid",
      "fiatCurrency": "SGD",
      "fiatAmount": "51",
      "currency": "USDT",
      "network": "TRC20",
      "address": "wallet-address",
      "cryptoAmount": "50.12",
      "exchangeRate": "1.02",
      "paymentMethod": "card",
      "hash": "blockchain-hash",
      "buyId": "provider-operation-id",
      "withdrawId": "provider-withdraw-id",
      "customerId": "customer-id",
      "merchantEarning": "0.9306",
      "merchantId": "merchant-id",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:07:00.000Z"
    }
  },
  "signature": "signature"
}
```

### `payment::failed`

```json theme={null}
{
  "id": "webhook-id",
  "delivered_at": "2026-04-16T10:02:05.000Z",
  "event": {
    "event_type": "payment::failed",
    "timestamp": "2026-04-16T10:02:05.000Z",
    "data": {
      "id": "crypto-cash-transaction-id",
      "externalId": "merchant-order-id",
      "status": "Fail",
      "fiatCurrency": "SGD",
      "fiatAmount": "51",
      "currency": "USDT",
      "network": "TRC20",
      "address": "wallet-address",
      "cryptoAmount": null,
      "exchangeRate": null,
      "paymentMethod": "card",
      "hash": null,
      "buyId": "provider-operation-id",
      "withdrawId": null,
      "customerId": "customer-id",
      "merchantEarning": null,
      "merchantId": "merchant-id",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:02:00.000Z"
    }
  },
  "signature": "signature"
}
```

### `payment::canceled`

```json theme={null}
{
  "id": "webhook-id",
  "delivered_at": "2026-04-16T10:03:05.000Z",
  "event": {
    "event_type": "payment::canceled",
    "timestamp": "2026-04-16T10:03:05.000Z",
    "data": {
      "id": "crypto-cash-transaction-id",
      "externalId": "merchant-order-id",
      "status": "Canceled",
      "fiatCurrency": "SGD",
      "fiatAmount": "51",
      "currency": "USDT",
      "network": "TRC20",
      "address": "wallet-address",
      "cryptoAmount": null,
      "exchangeRate": null,
      "paymentMethod": "card",
      "hash": null,
      "buyId": "provider-operation-id",
      "withdrawId": null,
      "customerId": "customer-id",
      "merchantEarning": null,
      "merchantId": "merchant-id",
      "createdAt": "2026-04-16T10:00:00.000Z",
      "updatedAt": "2026-04-16T10:03:00.000Z"
    }
  },
  "signature": "signature"
}
```
