> For the complete documentation index, see [llms.txt](https://ivorypay.gitbook.io/ivorypay-on-off-ramp-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ivorypay.gitbook.io/ivorypay-on-off-ramp-documentation/guides/deposits-and-settlements.md).

# Deposits & Settlements

Understanding fiat deposits via virtual accounts and their crypto settlement lifecycle.

Virtual accounts allow your business to receive fiat deposits that are automatically settled into crypto. This is ideal for businesses that want to accept local bank transfers and settle in stablecoins.

## How it works

```
1. Create a virtual account for a user (POST /v1/virtual-accounts)
2. User sends fiat to the virtual account via bank transfer
3. IvoryPay detects the deposit → webhook: deposit.received
4. Settlement begins: fiat is converted to crypto
5. Crypto is sent to your configured wallet
6. Settlement lifecycle webhooks fire at each stage
```

## Integration steps

{% stepper %}
{% step %}

### Create a virtual account

```bash
curl -X POST https://ramp-api.ivorypay.io/api/v1/virtual-accounts \
  -H "Authorization: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "merchant-user@example.com",
    "identityType": "bvn",
    "identityNumber": "22222222222",
    "currency": "NGN"
  }'
```

The user can now transfer fiat to this virtual account at any time.
{% endstep %}

{% step %}

### Receive deposit notifications

When a user transfers money to the virtual account, you will receive a webhook:

#### Webhook: `deposit.received`

```json
{
  "event": "deposit.received",
  "data": {
    "reference": "dep_abc123",
    "processorReference": "prov_xyz789",
    "accountNumber": "1234567890",
    "accountName": "IvoryPay - User Name",
    "currency": "NGN",
    "receivedAmount": 100000,
    "settledAmount": 99500,
    "status": "SUCCESS",
    "sourceAccountNumber": "0987654321",
    "sourceAccountName": "Sender Name",
    "sourceBankName": "GTBank",
    "paymentDate": "2026-03-26T14:30:00.000Z"
  }
}
```

{% endstep %}

{% step %}

### Track settlement lifecycle

After a deposit is received, IvoryPay automatically initiates settlement (converting fiat to crypto). The settlement progresses through these stages:

| Stage             | Webhook event                          | Description                                  |
| ----------------- | -------------------------------------- | -------------------------------------------- |
| Awaiting account  | `settlement.awaitingSettlementAccount` | Waiting for settlement account configuration |
| Converting        | `settlement.converting`                | Fiat-to-crypto conversion in progress        |
| Payout initiated  | `settlement.payoutInitiated`           | Crypto payout has been queued                |
| Payout processing | `settlement.payoutProcessing`          | Crypto is being sent on-chain                |
| Completed         | `settlement.success`                   | Crypto delivered to your wallet              |
| Failed            | `settlement.failed`                    | Settlement failed — check `failureReason`    |

#### Webhook: `settlement.success`

```json
{
  "event": "settlement.success",
  "data": {
    "reference": "set_abc123",
    "settlementId": "uuid-here",
    "depositId": "dep_abc123",
    "fiatAmount": 99500,
    "fiatCurrency": "NGN",
    "cryptoAmount": 62.18,
    "token": "USDT",
    "chain": "BSC_MAINNET",
    "destinationAddress": "0xYourWalletAddress...",
    "conversionRate": 1600,
    "platformFee": 0.31,
    "businessFee": 0,
    "gasFee": 0.5,
    "transactionHash": "0xsettlement123...",
    "status": "COMPLETED"
  }
}
```

{% endstep %}

{% step %}

### Query deposits and settlements

#### List all deposits

```bash
curl -X GET "https://ramp-api.ivorypay.io/api/v1/deposits" \
  -H "Authorization: your_api_key"
```

`GET /v1/deposits` does not currently accept query filters — it returns the merchant's deposits as wired by the controller.

#### Get settlement for a specific deposit

The `:depositId` path parameter is validated as a UUID.

```bash
curl -X GET "https://ramp-api.ivorypay.io/api/v1/settlements/deposit/f1e2d3c4-b5a6-7890-fedc-ba0987654321" \
  -H "Authorization: your_api_key"
```

{% endstep %}
{% endstepper %}

## Settlement lifecycle

```
DEPOSIT RECEIVED → AWAITING_SETTLEMENT_ACCOUNT → CONVERTING → PAYOUT_INITIATED
  → PAYOUT_PROCESSING → COMPLETED
                      → FAILED
```

## Frequently asked questions

<details>

<summary>How long does settlement take after a deposit is received?</summary>

Settlement typically completes within a few minutes after the deposit is confirmed. The exact time depends on blockchain network congestion and the conversion process. You can track progress in real time via the settlement lifecycle webhooks (`settlement.converting`, `settlement.payoutInitiated`, `settlement.payoutProcessing`, `settlement.success`).

</details>

<details>

<summary>What happens if a settlement fails?</summary>

If a settlement fails, you will receive a `settlement.failed` webhook with a `failureReason` field explaining the cause. Common reasons include wallet configuration issues or temporary network problems. Contact IvoryPay support if the issue persists, as failed settlements may need to be retried on their end.

</details>

<details>

<summary>Can a user send multiple deposits to the same virtual account?</summary>

Yes. Virtual accounts are persistent and can receive multiple deposits over time. Each deposit triggers its own `deposit.received` webhook and initiates a separate settlement cycle. There is no need to create a new virtual account for each transaction.

</details>

<details>

<summary>What does the settlement.awaitingSettlementAccount stage mean?</summary>

This stage means IvoryPay is waiting for a settlement account (destination crypto wallet) to be configured for your business. If you see settlements stuck in this stage, check your IvoryPay dashboard to ensure your settlement wallet address is properly set up.

</details>

<details>

<summary>Why is the settledAmount different from the receivedAmount?</summary>

The `settledAmount` may be less than the `receivedAmount` due to platform fees deducted by IvoryPay before settlement. The difference represents the fee charged for processing the deposit. You can see the detailed fee breakdown in the settlement webhook payload.

</details>

<details>

<summary>Can I query historical deposits and settlements via the API?</summary>

Yes. Use `GET /v1/deposits` to list all deposits for your merchant account (no query filters are currently wired). Use `GET /v1/settlements/deposit/{depositId}` — where `depositId` is a UUID — to retrieve the settlement details for a specific deposit.

</details>
