---
title: "See a deposit arrive"
description: "Follow a bank deposit from its webhook to its transaction row and its fee row, and match it to the sender."
---
This recipe follows a bank deposit from its webhook to its transaction row and its fee row, and matches it to the sender. It is for a US staffing platform whose employer clients wire USD into the platform's Gravv account.

**Status.** Available.

## Money example

Acme Staffing LLC wires 2,500 USD to your USD account.

1. Gravv receives the wire. Gravv does not convert a USD wire into a USD account, so it sends no `transfer.inflow.deposit_received`.
2. Gravv credits 2,500 USD to your account, sends `transfer.inflow.completed`, and writes an incoming `account_funding` row.
3. If your pricing has a fee for incoming wires, Gravv bills it to your business on a separate `fee_charge` row. The account keeps the full 2,500 USD.

## Before you start

- You have an `active` account with deposit details. To set one up, see [Share account details and receive a deposit](/recipes/accounts-v2/share-account-details-and-receive-a-deposit).
- You have registered your webhook URL in the Dashboard.

## Find out what is required

The deposit webhook carries the `transaction_id` of the row. The row tells you the rail in `channel` and the sender in `sending_account_name`.

## Steps

### 1. Converting accounts only: receive `transfer.inflow.deposit_received`

Some accounts convert what they receive. For example, an account receives USD and credits USDC. On those accounts, Gravv sends this event when the conversion starts. `event_data.amount` is the estimated converted amount, and `currency` is the currency Gravv credits. The event also carries `account_id`, `sender_name`, `sender_bank` and `narration`. A USD wire into a USD account skips this step.

### 2. Receive the credit

Gravv sends `transfer.inflow.completed`. On accounts that settle on chain, it sends `transfer.inflow.deposit_settled` with `tokenized: true` instead. Store `event_data.transaction_id` for the next step.

### 3. Read the row

Read the transaction row by that `transaction_id`.

<CodeGroup>
```bash title="cURL"
curl --request GET \
     --url https://api.gravv.xyz/v1/transactions/ec1b8ca1-f91e-4d4c-9ff4-9b7889463e85 \
     --header 'Api-Key: <API_KEY>'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/transactions/ec1b8ca1-f91e-4d4c-9ff4-9b7889463e85", {
  method: "GET",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
  },
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.get(
    "https://api.gravv.xyz/v1/transactions/ec1b8ca1-f91e-4d4c-9ff4-9b7889463e85",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

```json title="Response (trimmed)"
{
  "data": {
    "id": "ec1b8ca1-f91e-4d4c-9ff4-9b7889463e85",
    "customer_id": "5be6128e-18c2-4797-a142-ea7d17be3111",
    "amount": "2500",
    "date_created": "2026-10-13T08:02:11Z",
    "direction": "in",
    "transaction_type": "account_funding",
    "description": "Wire deposit from ACME STAFFING LLC",
    "sending_account_id": null,
    "receiving_account_id": "43b7a3a6-9a8d-4a03-980d-7b71d8f56413",
    "sending_account_name": "ACME STAFFING LLC",
    "sending_account_number": "000123456789",
    "channel": "wire",
    "status": "completed",
    "currency": "USD"
  },
  "error": null
}
```

On accounts that do not settle on chain, `description` reads like `Inbound USD deposit settlement` and `channel` is `bank_transfer`. When the sender is unknown, `sending_account_name` is `External Sender`. Do not rely on `balance_before` and `balance_after` on deposit rows.

### 4. Find the fee

<CodeGroup>
```bash title="cURL"
curl --request GET \
     --url 'https://api.gravv.xyz/v1/transactions?customer_id=5be6128e-18c2-4797-a142-ea7d17be3111&transaction_type=fee_charge' \
     --header 'Api-Key: <API_KEY>'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/transactions?customer_id=5be6128e-18c2-4797-a142-ea7d17be3111&transaction_type=fee_charge", {
  method: "GET",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
  },
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.get(
    "https://api.gravv.xyz/v1/transactions?customer_id=5be6128e-18c2-4797-a142-ea7d17be3111&transaction_type=fee_charge",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

Gravv never takes a deposit fee out of the deposit, so Gravv credits the account with the full 2,500 USD. Gravv takes the fee from your business's main account, or puts it on your monthly invoice if your plan bills that way. Look for the fee row by the deposit row's `customer_id`, not by the receiving account. The fee row has a `description` that starts with `Fee charge:` and a `date_created` shortly after the deposit. A fee on the monthly invoice has no row here. See [Add up a month of fees](/recipes/transactions/add-up-a-month-of-fees).

### 5. List all deposits on the account

<CodeGroup>
```bash title="cURL"
curl --request GET \
     --url 'https://api.gravv.xyz/v1/transactions?account_id=43b7a3a6-9a8d-4a03-980d-7b71d8f56413&direction=in&transaction_type=account_funding,crypto_funding' \
     --header 'Api-Key: <API_KEY>'
```
```javascript title="Node"
const response = await fetch("https://api.gravv.xyz/v1/transactions?account_id=43b7a3a6-9a8d-4a03-980d-7b71d8f56413&direction=in&transaction_type=account_funding,crypto_funding", {
  method: "GET",
  headers: {
    "Api-Key": process.env.GRAVV_API_KEY,
  },
});
console.log(response.status, await response.json());
```
```python title="Python"
import os

import requests

response = requests.get(
    "https://api.gravv.xyz/v1/transactions?account_id=43b7a3a6-9a8d-4a03-980d-7b71d8f56413&direction=in&transaction_type=account_funding,crypto_funding",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

## What can go wrong

| Response | Why | Fix |
| :-- | :-- | :-- |
| `transfer.inflow.deposit_received`, then nothing for a long time | The conversion has started and Gravv is waiting for it to settle | Nothing to send. If it does not complete, contact support with the `transaction_id` and the amount |
| `transfer.inflow.awaiting_approval` | Your account holds deposits for approval | Approve it in the Dashboard |
| `transfer.inflow.payout_failed` (with `error` and `failed_at`) or `transfer.inflow.compensated` | Crediting failed, or was reversed | Contact support with the `transaction_id` |
| Two webhooks for one on-chain deposit: `inflow.onchain.completed` and a `transfer.inflow.*` event | Two parts of Gravv each send an event for it | Link them by `settlement_event_group_id` (the on-chain event's `event_group_id`) or by `tx_hash` |
| A row typed `crypto_funding` becomes `account_funding` later | Gravv relabels the on-chain credit once the bank side identifies it | Key on `id`, never on the type |
| No fee row for some deposits | Some rails carry no fee on your pricing | No action needed |

## Webhooks you will receive

Gravv sends these events:

- `transfer.inflow.deposit_received` when Gravv converts the deposit, or `transfer.inflow.awaiting_approval` on accounts that hold deposits for approval.
- Then `transfer.inflow.completed` or `transfer.inflow.deposit_settled`.
- On a failure, `transfer.inflow.payout_failed` or `transfer.inflow.compensated`.
- For on-chain wallet deposits, `inflow.onchain.completed` or `inflow.onchain.failed`, with `status` in upper case.

In webhook history, deposit events are in the `transfer` category. See [Deposit events](/platform/webhooks/event-catalogue#deposits-into-accounts).

## Testing in sandbox

Simulate the wire with your sandbox key. Each new `Idempotency-Key` makes a new 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: bc940866-b0d8-4b6a-ac1c-fe56c774550c' \
     --header 'Content-Type: application/json' \
     --data '
{
  "account_id": "43b7a3a6-9a8d-4a03-980d-7b71d8f56413",
  "amount": "2500.00",
  "rail": "wire"
}
'
```
```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": "bc940866-b0d8-4b6a-ac1c-fe56c774550c",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "account_id": "43b7a3a6-9a8d-4a03-980d-7b71d8f56413",
    "amount": "2500.00",
    "rail": "wire"
  }),
});
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": "bc940866-b0d8-4b6a-ac1c-fe56c774550c",
        "Content-Type": "application/json",
    },
    json={
        "account_id": "43b7a3a6-9a8d-4a03-980d-7b71d8f56413",
        "amount": "2500.00",
        "rail": "wire",
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

The response is `{"data":{"accepted":true,"reference":"…","state":"pending"},"error":null}`. Wait for the webhooks, then run steps 3 to 5.

For example, a simulated 5,000 NGN deposit sent `transfer.inflow.completed` with these values:

- `sender_name` was `Sandbox Test Sender`.
- `narration` was `sandbox simulated pay-in`.
- `session_id` was equal to the simulator's `reference`.
- `transaction_id` was equal to the row's `id`.

The row had `transaction_type` `account_funding`, `channel` `bank_transfer` and `status` `completed`. The deposit carried no fee, so no `fee_charge` row appeared. A deposit event's `idempotency_key` is a long hash, not `<reference>:<sequence>`.

## Reference

- [Get a transaction](/api-reference/transactions/get-v1-transactions-transaction-id)
- [List transactions](/api-reference/transactions/get-v1-transactions)
- [Simulate a deposit](/api-reference/accounts-v2/post-v2-accounts-simulate-deposit)
- [Webhook history](/api-reference/webhooks/get-v1-webhooks-history)
