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
- Your customer Acme Ltd has an active USD account. You show its routing and account numbers.
- Acme's bank sends a 2,500.00 USD wire.
- Gravv credits the account the full 2,500.00 USD and sends
transfer.inflow.completed, ortransfer.inflow.deposit_settledif the account converts deposits to USDC. - 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
- The account is
activeand has its bank details. To set this up, see Share account details and receive a deposit. - You have registered your webhook URL in the Dashboard.
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.completedortransfer.inflow.deposit_settled. On accounts that convert deposits,inflow.onchain.completedcomes with it. - On a failure,
transfer.inflow.payout_failedortransfer.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.