---
title: "Add up a month of fees"
description: "List every fee Gravv charged an account in a month, tie each to its movement, and subtract fee refunds."
---
Use this recipe to list every fee Gravv charged an account in a month, match each fee to its movement, and subtract fee refunds.

**Who it is for.** This recipe is for a finance lead who checks Gravv's fees against the agreed price list at the end of the month.

**Status.** Available.

## Money example

In October, from the USD account:

1. You send 40 payouts, each with `fee` `"1.5"` on its row, for 60 USD in all.
2. You receive 3 incoming wires, each followed by its own `fee_charge` row.
3. One payout failed. If its fee came back, there is a `fee_charge` row with `direction` `in`.

Your total is the sum of the `out` fee rows, minus the sum of the `in` fee rows.

## Before you start

- Your agreed price list.
- The id of the account Gravv takes your fees from. That is your business's main account for deposit fees, and the sending account for transfer fees.

Amounts come from your business's own pricing, so this recipe never quotes a price.

## Find out what is required

- On a transfer's row, `fee` is the fee for that transfer, as a string. It is `"0"` when there is no fee. The transfer response gives its `fee_currency`.
- Every settled fee also has its own row, with `transaction_type` `fee_charge`, `direction` `out`, `channel` `internal`, `status` `completed`, `reference` set to the fee's id, and a `description` in the form `Fee charge: <service> - <fee type>`.
- A fee refund is a `fee_charge` row with `direction` `in` and a `description` that starts `Fee refund:`.
- Do not add both a transfer's `fee` and its `fee_charge` row. They describe the same fee.
- A fee row's `sending_account_id` is the account Gravv took the fee from, and its `customer_id` is the customer the movement was for. Gravv takes deposit fees from your business's main account, never from the deposit.
- A fee your plan bills on the monthly invoice, instead of taking it at once, has no `fee_charge` row. You find it on your invoice instead.

## Steps

### 1. List the fee rows

The list has no date filter. Page through it, newest first, until `date_created` is before `2026-10-01T00:00:00Z`.

<CodeGroup>
```bash title="cURL"
curl --request GET \
     --url 'https://api.gravv.xyz/v1/transactions?account_id=43b7a3a6-9a8d-4a03-980d-7b71d8f56413&transaction_type=fee_charge&items_per_page=100&page=1' \
     --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&transaction_type=fee_charge&items_per_page=100&page=1", {
  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&transaction_type=fee_charge&items_per_page=100&page=1",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

```json title="Response"
{
  "data": {
    "items": [
      {
        "id": "4b0dbb41-8d52-48f1-942c-3fe860e7a113",
        "amount": "15",
        "date_created": "2026-10-13T08:02:40Z",
        "direction": "out",
        "transaction_type": "fee_charge",
        "description": "Fee charge: transfers - wire_in",
        "reference": "e2acf72f-9e57-4f7a-a0ee-89aed453dd32",
        "channel": "internal",
        "status": "completed",
        "currency": "USD"
      }
    ],
    "page": 1,
    "items_per_page": 1,
    "total_pages": 1,
    "total_items": 1
  },
  "error": null
}
```

### 2. Add them up

For each fee type in `description`, add the `out` rows and subtract the `in` rows. Compare each total with your price list.

### 3. Cross-check the payouts

<CodeGroup>
```bash title="cURL"
curl --request GET \
     --url 'https://api.gravv.xyz/v1/transactions?account_id=43b7a3a6-9a8d-4a03-980d-7b71d8f56413&direction=out&transaction_type=global_remittance,bank_account&items_per_page=100&page=1' \
     --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=out&transaction_type=global_remittance,bank_account&items_per_page=100&page=1", {
  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=out&transaction_type=global_remittance,bank_account&items_per_page=100&page=1",
    headers={
        "Api-Key": os.environ["GRAVV_API_KEY"],
    },
)
print(response.status_code, response.json())
```
</CodeGroup>

Read `fee` on each row. US bank payouts are `bank_account` rows, and cross-border payouts are `global_remittance` rows.

## What can go wrong

| Response | Why | Fix |
| :-- | :-- | :-- |
| A payout shows `fee` `"0"` | No fee is set for that route, or Gravv could not work out the fee when you sent the payout. In both cases, Gravv charges no fee | This is expected. You owe nothing for that payout |
| A fee row with no movement in the same month | Gravv writes a fee row when it charges the fee, and the fee can settle after its movement | Match fee rows to movements within one day either side, not on the exact day |
| A fee is in a different currency from the payout | Percentage fees use the source currency, and conversions charge in the asset sent | Read `fee_currency` in the transfer response |
| The volume total includes fees | `/v1/transactions/volume` adds every transaction type unless you filter | Always pass `transaction_type` |

## Webhooks you will receive

Gravv sends no webhook for fees.

## Testing in sandbox

Run a [simulated deposit](/recipes/transactions/see-a-deposit-arrive#testing-in-sandbox) and a small sandbox payout. Fee rows appear only when your sandbox pricing has fees. In sandbox, a 5 USD payout showed `fee` `"1"` on its own row, but no `fee_charge` row for it appeared within minutes. A KYC fee did appear as its own row (`Fee charge: identity - kyc_individual`). So in sandbox, read transfer fees from the transfer rows.

## Reference

- [List transactions](/api-reference/transactions/get-v1-transactions)
- [Get a transaction](/api-reference/transactions/get-v1-transactions-transaction-id)
- [Transaction volume](/api-reference/transactions/get-v1-transactions-volume)
