---
title: "Make a test deposit land"
description: "Simulate an incoming bank deposit on a sandbox account so the real pipeline runs, and know which accounts can take one today."
---
Use this recipe to simulate an incoming bank deposit on a sandbox account, so the real deposit pipeline runs, and to find out which accounts can take one today.

**Who it is for.** A payroll app that opens accounts for freelancers and needs to see deposits, fee rows and webhooks before going live.

**Status.** Available for NGN accounts. Not yet available for USD, EUR and GBP accounts, because they do not open in sandbox today.

## Money example

1. Chidi's sandbox NGN account is `active` with test bank details.
2. You simulate a 50,000.00 NGN deposit into it.
3. Gravv credits the account, writes an `account_funding` row and sends `transfer.inflow.completed`.
4. `GET /v2/accounts/{id}` shows the new balance.

## Before you start

- A sandbox key. If you use a scoped key, it needs `transfer:initiate`.
- An `active` sandbox account. Today, these rules apply:
  - Sandbox KYC does not complete, so a **new** sandbox customer cannot open any account. Ask support for an already verified sandbox customer.
  - For a verified customer, an NGN account opens within seconds.
  - USD, EUR and GBP accounts go to review in sandbox and stay `pending`.

## Find out what is required

- `rail` is optional. It takes `ach`, `wire`, `rtp`, `swift`, `sepa` or `fps`, and it sets the rail and so the fee. Leave it out for NGN.
- `amount` is a string with at most 2 decimal places. `currency` defaults to the account's currency.
- If the account's bank has no simulator, the call returns an error that says so.

## Steps

### 1. Simulate the deposit

<CodeGroup>
```bash title="cURL"
curl --request POST \
     --url https://api.gravv.xyz/v2/accounts/simulate/deposit \
     --header 'Api-Key: <API_KEY>' \
     --header 'Idempotency-Key: 731f71da-768f-43ef-a78a-63ba4f793434' \
     --header 'Content-Type: application/json' \
     --data '
{
  "account_id": "8d0e6b4a-2f1c-4e3d-9a5b-7c6d5e4f3a21",
  "amount": "50000.00",
  "currency": "NGN"
}
'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v2/accounts/simulate/deposit", {
  method: "POST",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
    "Idempotency-Key": "731f71da-768f-43ef-a78a-63ba4f793434",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "account_id": "8d0e6b4a-2f1c-4e3d-9a5b-7c6d5e4f3a21",
    "amount": "50000.00",
    "currency": "NGN"
  }),
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.post(
    "https://api.gravv.xyz/v2/accounts/simulate/deposit",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
        "Idempotency-Key": "731f71da-768f-43ef-a78a-63ba4f793434",
        "Content-Type": "application/json",
    },
    json={
        "account_id": "8d0e6b4a-2f1c-4e3d-9a5b-7c6d5e4f3a21",
        "amount": "50000.00",
        "currency": "NGN",
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

```json title="Response"
{
  "data": {
    "accepted": true,
    "reference": "d3b07384-d9a0-4c8f-9e2b-6f1a2c3d4e5f",
    "state": "pending"
  },
  "error": null
}
```

`accepted` means Gravv received the deposit, not that it credited it. Each new `Idempotency-Key` makes a new deposit. The same key returns the same `reference`.

### 2. Wait for the webhook

The webhook depends on the account:

- For an account whose money is held at a bank, Gravv sends `transfer.inflow.completed`.
- For a wallet-backed account, Gravv sends `transfer.inflow.deposit_settled`.
- For an account that holds deposits for approval, Gravv sends `transfer.inflow.awaiting_approval` first. Approve the deposit in the Dashboard.

In the event, `session_id` is the simulator's `reference`, and `transaction_id` is the row's `id`.

### 3. Read the balance and the row

Read the balance from `GET /v2/accounts/{id}`, not from the row. A simulated deposit row shows `balance_before` and `balance_after` as `"0"`. To read the row and its fee, see [See a deposit arrive](/recipes/transactions/see-a-deposit-arrive#3-read-the-row).

For a step-by-step version with every field, see [Test accounts with a simulated deposit](/recipes/accounts-v2/test-accounts-with-a-simulated-deposit).

## What can go wrong

| Response | Why | Fix |
| :-- | :-- | :-- |
| 403 `Forbidden: insufficient permissions to perform this action` | You used a live key | Use a `grvSec_sandbox_` key |
| 403 with `required_any` `["transfer:initiate"]` | The scoped key lacks the permission | Add the permission to the key |
| 400 `invalid request payload: cannot unmarshal number …` | You sent `amount` as a number | Send `"50000.00"` |
| 400 `amount must be a positive decimal number` | The amount is zero or negative | Send a positive decimal string |
| 400 `amount cannot be expressed on this account's receiving rail` | The amount has more than 2 decimal places | Send at most 2 decimal places |
| 400 `the simulated deposit was not recorded; the amount may be below what this account's rail credits` | The amount is too small to be credited | Send a larger amount |
| 400 `rail must be one of ach, wire, rtp, swift, sepa, fps` | The rail is unknown | Send a listed rail |
| 400 `account has no linked virtual account to deposit into` | The account is not `active` yet | Wait for `applications.accounts.completed` |
| 400 `deposit simulation is not supported for this account's receiving rail` | This account has no simulator | Test with an NGN v2 account |
| 404 `Resource not found` | The id is wrong, is a live id, or belongs to another business | Use your sandbox account id |

## Webhooks you will receive

- `transfer.inflow.completed` on an account whose money is held at a bank.
- `transfer.inflow.deposit_settled` with `inflow.onchain.completed` on a wallet-backed account.
- `transfer.inflow.awaiting_approval` first, on accounts that hold deposits for approval.

## Testing in sandbox

The deposit simulator works only in sandbox. There are no special amounts. Every valid deposit succeeds. You cannot produce a returned or failed deposit, so test those screens with recorded `transfer.inflow.payout_failed` and `transfer.inflow.compensated` payloads.

## Reference

- [Simulate a deposit](/api-reference/accounts-v2/post-v2-accounts-simulate-deposit)
- [Test accounts in sandbox](/platform/accounts/test-accounts-in-sandbox)
