Know what a bank deposit costs

Find out which bank deposits into a customer's account carry a Gravv fee, on which rail, who pays the fee and where to find it.

This recipe shows which bank deposits into a customer's account carry a Gravv fee, on which rail, who pays the fee and where to find it. It is for a US payroll platform whose business customers fund their Gravv USD account by wire from their own bank.

Status. Available.

Money example

  1. Your customer Acme Ltd has an active USD account. You show its routing and account numbers.
  2. Acme's bank sends a 2,500.00 USD wire.
  3. Gravv credits the account the full 2,500.00 USD and sends transfer.inflow.completed, or transfer.inflow.deposit_settled if the account converts deposits to USDC.
  4. Gravv bills the wire deposit fee on your plan to your business, from your main account or on your monthly invoice. Gravv does not take it out of Acme's 2,500.00 USD.

Before you start

Find out what is required

The fee depends on the rail the money arrives on, and on the kind of account. Your prices per rail are in your pricing plan. The API does not return them.

Rail Account that converts deposits to stablecoin Other bank-held account NGN account
ACH Charged Not charged today Not applicable
Wire, RTP, SWIFT, SEPA, FPS Charged, at that rail's price Charged, at that rail's price Not applicable
Rail not known Charged a flat deposit fee Not charged Not applicable
Local NGN transfer Not applicable Not applicable Not charged
  • Gravv bills the fee to your business and does not deduct it from the deposit.
  • Gravv does not refund the fee if the deposit later fails or is reversed.

Steps

1. Credit the customer when the deposit lands

Follow Share account details and receive a deposit. On an account that converts deposits, transfer.inflow.deposit_settled and inflow.onchain.completed describe one deposit, so credit the customer once.

2. Find the fee

Find the deposit row with the transaction_id from the webhook, then the fee row for the same customer, as in See a deposit arrive.

curl --request GET \
 --url 'https://api.gravv.xyz/v1/transactions?customer_id=121ae3e6-03a6-4966-a13b-ca7fd644de2f&transaction_type=fee_charge' \
 --header 'Api-Key: <API_KEY>'
const response = await fetch("https://api.gravv.xyz/v1/transactions?customer_id=121ae3e6-03a6-4966-a13b-ca7fd644de2f&transaction_type=fee_charge", {
method: "GET",
headers: {
"Api-Key": process.env.GRAVV_API_KEY,
},
});
console.log(response.status, await response.json());
import os

import requests

response = requests.get(
"https://api.gravv.xyz/v1/transactions?customer_id=121ae3e6-03a6-4966-a13b-ca7fd644de2f&transaction_type=fee_charge",
headers={
    "Api-Key": os.environ["GRAVV_API_KEY"],
},
)
print(response.status_code, response.json())

What can go wrong

Response Why Fix
No bank details on the account The account is not active yet Finish the account application
transfer.inflow.awaiting_approval Your account holds deposits for approval Approve or reject it in the Dashboard. The API has no approve route
transfer.inflow.payout_failed or transfer.inflow.compensated Crediting failed, or was reversed Contact support with the transaction_id. Gravv does not refund the deposit fee
No webhook, but the payer says they sent the money The payer used wrong bank details, the bank is still processing, or your server missed a webhook Check webhook history with event_type=transfer, and check the details the payer used
No fee row Your plan has no fee for that rail, or the fee is on your monthly invoice No action needed

Webhooks you will receive

  • transfer.inflow.awaiting_approval, only on accounts that hold deposits for approval.
  • Then transfer.inflow.completed or transfer.inflow.deposit_settled. On accounts that convert deposits, inflow.onchain.completed comes with it.
  • On a failure, transfer.inflow.payout_failed or transfer.inflow.compensated.

Testing in sandbox

Simulate a deposit on each rail (ach, wire, rtp, swift, sepa, fps) to see that rail's fee on your sandbox billing. Each new Idempotency-Key makes a new deposit, and amount is a string.

curl --request POST \
 --url https://api.gravv.xyz/v2/accounts/simulate/deposit \
 --header 'Api-Key: <API_KEY>' \
 --header 'Idempotency-Key: 0252c0da-374a-4eac-bf9a-2203e6881143' \
 --header 'Content-Type: application/json' \
 --data '
{
"account_id": "416e99b0-e13e-413e-bdaa-ea00a01d616f",
"amount": "2500.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": "0252c0da-374a-4eac-bf9a-2203e6881143",
"Content-Type": "application/json",
},
body: JSON.stringify({
"account_id": "416e99b0-e13e-413e-bdaa-ea00a01d616f",
"amount": "2500.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": "0252c0da-374a-4eac-bf9a-2203e6881143",
    "Content-Type": "application/json",
},
json={
    "account_id": "416e99b0-e13e-413e-bdaa-ea00a01d616f",
    "amount": "2500.00",
    "rail": "wire",
},
)
print(response.status_code, response.json())

The response {"data":{"accepted":true,"reference":"…","state":"pending"},"error":null} does not mean the account is credited. Wait for the webhook. With a live key, this call returns 403.

Reference

Was this page helpful?