Test accounts with a simulated deposit

Send a fake incoming deposit to a sandbox account and see the real fees, ledger entries, webhooks and balance.

Send a simulated incoming deposit to a sandbox account, and see the real fees, ledger entries, webhooks and balance that it produces.

API version: v2.

Who it is for. A developer who needs deposit webhooks and balances in sandbox without a real bank transfer.

Status. Available in sandbox only.

Money example

You simulate a 250.00 USD deposit into Ada's sandbox account.

  1. You call simulate with "amount": "250.00" and "rail": "wire".
  2. Her account is wallet-backed, so sandbox first delivers test USDC to its wallet, then records the bank deposit.
  3. You receive transfer.inflow.deposit_settled, and the balance rises by the full 250.00 USD. Gravv bills any deposit fee on your plan to your business separately, on its own fee_charge row.

Before you start

  • You use a sandbox key (grvSec_sandbox_). A scoped key needs transfer:initiate.
  • You have an active sandbox account with bank details. Check it with GET /v2/accounts/{id}. Sandbox KYC does not complete today. The only account you can usually open is an NGN account, which sandbox opens at once. See Test accounts in sandbox.

Find out what is required

The rail you send depends on the account's currency and its option's rails:

Account rail
USD ach, wire, rtp or swift, from the option's rails
EUR sepa
GBP fps
NGN, KES Leave rail out. For NGN, send "currency": "NGN" and an amount in naira

Steps

1. Simulate the deposit

Each new key makes a new deposit. The same key returns the same reference.

curl --request POST \
 --url https://api.gravv.xyz/v2/accounts/simulate/deposit \
 --header 'Api-Key: <API_KEY>' \
 --header 'Idempotency-Key: 755660af-95ef-468d-9fd2-cefd7c7e4c4f' \
 --header 'Content-Type: application/json' \
 --data '
{
"account_id": "6f1c2a9e-3b1d-4f7a-9c62-0d8e4b7a5c11",
"amount": "250.00",
"rail": "wire"
}
'
const response = await fetch("https://api.gravv.xyz/v2/accounts/simulate/deposit", {
method: "POST",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Idempotency-Key": "755660af-95ef-468d-9fd2-cefd7c7e4c4f",
"Content-Type": "application/json",
},
body: JSON.stringify({
"account_id": "6f1c2a9e-3b1d-4f7a-9c62-0d8e4b7a5c11",
"amount": "250.00",
"rail": "wire"
}),
});
console.log(response.status, await response.json());
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": "755660af-95ef-468d-9fd2-cefd7c7e4c4f",
    "Content-Type": "application/json",
},
json={
    "account_id": "6f1c2a9e-3b1d-4f7a-9c62-0d8e4b7a5c11",
    "amount": "250.00",
    "rail": "wire",
},
)
print(response.status_code, response.json())
{
  "data": {
    "accepted": true,
    "reference": "d3b07384-d9a0-4c8f-9e2b-6f1a2c3d4e5f",
    "state": "pending"
  },
  "error": null
}

To simulate an NGN deposit, send currency and no rail:

curl --request POST \
 --url https://api.gravv.xyz/v2/accounts/simulate/deposit \
 --header 'Api-Key: <API_KEY>' \
 --header 'Idempotency-Key: a744cbfa-39bb-4923-a45b-cbf2fe937e1a' \
 --header 'Content-Type: application/json' \
 --data '
{
"account_id": "2d4f6a8c-0e1b-4c3d-9e5f-7a9b1c3d5e7f",
"amount": "50000.00",
"currency": "NGN"
}
'
const response = await fetch("https://api.gravv.xyz/v2/accounts/simulate/deposit", {
method: "POST",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
"Idempotency-Key": "a744cbfa-39bb-4923-a45b-cbf2fe937e1a",
"Content-Type": "application/json",
},
body: JSON.stringify({
"account_id": "2d4f6a8c-0e1b-4c3d-9e5f-7a9b1c3d5e7f",
"amount": "50000.00",
"currency": "NGN"
}),
});
console.log(response.status, await response.json());
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": "a744cbfa-39bb-4923-a45b-cbf2fe937e1a",
    "Content-Type": "application/json",
},
json={
    "account_id": "2d4f6a8c-0e1b-4c3d-9e5f-7a9b1c3d5e7f",
    "amount": "50000.00",
    "currency": "NGN",
},
)
print(response.status_code, response.json())

2. Wait for the webhooks

accepted does not mean that Gravv credited the deposit. Wait for the deposit webhooks, as in Share account details and receive a deposit.

3. Read the balance

Read the account with GET /v2/accounts/{id}. ledger_balance.projected rises first, then posted.

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 {"reason": "forbidden", "required_any": ["transfer:initiate"]} A scoped key without the permission Add it
400 invalid request payload: cannot unmarshal number into … amount of type string You sent amount as a number Send a string, such as "250.00"
400 rail must be one of ach, wire, rtp, swift, sepa, fps You sent an unknown rail Send a listed rail
400 account has no linked virtual account to deposit into The account is not active yet Wait for activation
400 account has no wallet to deliver a simulated deposit to The wallet-backed account has no wallet Open a new account on another network
400 amount cannot be expressed on this account's receiving rail The amount has too many decimals Send at most 2 decimals
400 the simulated deposit was not recorded; the amount may be below what this account's rail credits The amount is too small Send a larger amount
400 deposit simulation is not supported for this account's receiving rail This account has no simulator Test with another option
404 Resource not found The account_id is wrong, live or not yours. The API looks up the account before it checks the amount Send a sandbox account id
The same reference comes back You reused the same key Send a new key for each test deposit

Webhooks you will receive

  • Wallet-backed account. You receive inflow.onchain.completed, then transfer.inflow.deposit_settled (or transfer.inflow.completed).
  • Fiat-only account. You receive transfer.inflow.completed.
  • Deposit held for approval. You receive transfer.inflow.awaiting_approval first.

Testing in sandbox

This recipe is the sandbox tool. No special test value changes its outcome. NGN accounts open at once in sandbox. For the other options, sandbox calls the bank's test system, so they do not open at once.

Reference

Was this page helpful?