# Bank transactions

> Spend money, receive money and transfer between bank accounts — and fetch the posted journal entry — through the AccountFUN public API.

Source: https://www.accountfun.com/docs/api/bank-transactions/

The bank-transaction endpoints post money movements **straight to the ledger** with no bill or invoice behind them: a bank fee, cash expense or owner drawing (**Spend Money**), interest, a refund or an owner contribution (**Receive Money**), or money moved between two of the organization’s bank accounts (**Transfer Money**). They are the API equivalent of the same actions inside AccountFUN.

`GET /api/v1/transactions/{id}` reads the posted journal entry — including the `journalEntryId` a payment, spend, receive or transfer returned — with its debit/credit lines.

Both write endpoints need the `bank_transactions:write` scope, an **opt-in** on the consent screen, and the authorizing user’s role must have the **Bank Accounts** permission. Reading a transaction needs `bank_transactions:read` (with the same role ceiling). See [Authentication](https://www.accountfun.com/docs/api/authentication/).

One key per money movement

Both `POST /api/v1/bank-transactions` and `POST /api/v1/transfers` support the [`Idempotency-Key`](https://www.accountfun.com/docs/api/errors-and-limits/#idempotency-and-retries) header, so a retried request can’t post the same money twice. Send a new key for each new movement.

## Endpoints

| Method | Path                        | Scope                     | Description                                    |
| ------ | --------------------------- | ------------------------- | ---------------------------------------------- |
| `POST` | `/api/v1/bank-transactions` | `bank_transactions:write` | Post a `spend_money` or `receive_money` entry. |
| `POST` | `/api/v1/transfers`         | `bank_transactions:write` | Transfer money between two bank accounts.      |
| `GET`  | `/api/v1/transactions/{id}` | `bank_transactions:read`  | Fetch a posted journal entry with its lines.   |

Related lookups you will usually need: the [chart of accounts](https://www.accountfun.com/docs/api/chart-of-accounts/) (the bank account and each line’s account), [contacts](https://www.accountfun.com/docs/api/contacts/) (who the money was with) and [tax rates](https://www.accountfun.com/docs/api/tax-rates/). AI assistants can use all of this through the [MCP server](https://www.accountfun.com/docs/api/mcp/).

***

## Spend money / Receive money

Posts one balanced `spend_money` (money out) or `receive_money` (money in) journal entry against a bank account. Use this when there’s **no bill or invoice**: for a supplier invoice or receipt, create a [bill](https://www.accountfun.com/docs/api/bills/) instead; to settle an invoice or bill, record a [payment](https://www.accountfun.com/docs/api/payments/).

### Request

```bash
curl -X POST https://app.accountfun.com/api/v1/bank-transactions \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: bankfee-2026-10-08" \
  -d '{
    "type": "spend_money",
    "bankAccountId": "00000000-0000-0000-0000-00000000000b",
    "contactId": "00000000-0000-0000-0000-0000000000c1",
    "date": "2026-10-08",
    "reference": "STMT-2026-10",
    "description": "Monthly account fee",
    "lines": [
      {
        "accountId": "00000000-0000-0000-0000-000000000077",
        "amount": "12.50",
        "description": "Bank charges"
      }
    ]
  }'
```

### Body parameters

| Field           | Type                | Required | Description                                                                      |
| --------------- | ------------------- | -------- | -------------------------------------------------------------------------------- |
| `type`          | string              | Yes      | `spend_money` (money out) or `receive_money` (money in).                         |
| `bankAccountId` | string              | Yes      | The bank account the money moves through.                                        |
| `contactId`     | string              | Yes      | The contact the money is with.                                                   |
| `date`          | date (`YYYY-MM-DD`) | Yes      | The transaction date.                                                            |
| `lines`         | array               | Yes      | 1–20 lines (see below).                                                          |
| `reference`     | string              | No       | Free-text reference on the journal entry.                                        |
| `description`   | string              | No       | Description on the journal entry.                                                |
| `taxMode`       | string              | No       | `none`, `exclusive` or `inclusive` — how the line amounts relate to tax.         |
| `exchangeRate`  | string              | No       | Bank-to-base rate for a foreign-currency bank account. Omit for the system rate. |

### Line parameters

| Field               | Type   | Required | Description                                                                                                                      |
| ------------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `accountId`         | string | Yes      | The account to code the line to — an expense or asset account for `spend_money`, an income or asset account for `receive_money`. |
| `amount`            | string | Yes      | **Signed** amount, in the bank account’s currency: positive for a spend line, negative for an offsetting line (e.g. a discount). |
| `description`       | string | No       | Line description.                                                                                                                |
| `taxRateId`         | string | No       | A tax rate from [`GET /api/v1/tax-rates`](https://www.accountfun.com/docs/api/tax-rates/). Omit for no tax.                      |
| `quantity`          | string | No       | Informational quantity.                                                                                                          |
| `unitAmount`        | string | No       | Informational unit amount.                                                                                                       |
| `trackingOption1Id` | string | No       | Tracking option 1.                                                                                                               |
| `trackingOption2Id` | string | No       | Tracking option 2.                                                                                                               |

Line amounts are signed decimal strings in the bank account’s currency, and the **net total must be positive** — so every entry has a clear direction.

### Response — `201 Created`

```json
{
  "journalEntry": {
    "id": "je-7f3a91",
    "type": "spend_money",
    "date": "2026-10-08",
    "reference": "STMT-2026-10",
    "description": "Monthly account fee",
    "currency": "USD"
  }
}
```

The posted entry can be read in full with [`GET /api/v1/transactions/{id}`](#fetch-a-transaction).

### Errors

| Status | Cause                                                                                                                                       |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid body — the `details` object names each bad field.                                                                                   |
| `400`  | The net line total isn’t positive, an account can’t be used on a bank transaction, or a date is invalid.                                    |
| `403`  | The role lacks the **Bank Accounts** permission, the bank account isn’t in this organization, or the date falls on or before the lock date. |
| `404`  | The bank account or a referenced record does not exist in the token’s organization.                                                         |

***

## Transfer money between bank accounts

Posts a balanced `transfer` journal entry between two of the organization’s bank accounts. Both accounts must be Bank accounts in the token’s organization and must be different.

### Request

```bash
curl -X POST https://app.accountfun.com/api/v1/transfers \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: transfer-2026-10-08-payroll" \
  -d '{
    "fromAccountId": "00000000-0000-0000-0000-00000000000b",
    "toAccountId": "00000000-0000-0000-0000-00000000000c",
    "amount": "5000.00",
    "date": "2026-10-08",
    "reference": "Payroll funding"
  }'
```

### Body parameters

| Field           | Type                | Required | Description                                                                        |
| --------------- | ------------------- | -------- | ---------------------------------------------------------------------------------- |
| `fromAccountId` | string              | Yes      | The bank account the money leaves.                                                 |
| `toAccountId`   | string              | Yes      | The bank account the money arrives in. Must differ from `fromAccountId`.           |
| `amount`        | string              | Yes      | Amount in the **source account’s** currency.                                       |
| `date`          | date (`YYYY-MM-DD`) | Yes      | The transfer date.                                                                 |
| `reference`     | string              | No       | Free-text reference.                                                               |
| `description`   | string              | No       | Description on the journal entry.                                                  |
| `exchangeRate`  | string              | No       | Source-to-destination rate for a cross-currency transfer. Omit for the day’s rate. |

For a **cross-currency** transfer, `amount` is measured in the source currency and the destination receives `amount × exchangeRate`. A rate (yours or the day’s) that implies an FX residual of more than **5%** is refused with `400` — verify the rate and retry with a corrected `exchangeRate`.

### Response — `201 Created`

```json
{
  "transfer": {
    "journalEntryId": "je-9b21c4",
    "fromAccountId": "00000000-0000-0000-0000-00000000000b",
    "toAccountId": "00000000-0000-0000-0000-00000000000c",
    "sourceCurrency": "USD",
    "destinationCurrency": "USD",
    "sourceAmount": "5000.00",
    "destinationAmount": "5000.00",
    "exchangeRate": null
  }
}
```

`exchangeRate` is the effective source-to-destination rate when the two accounts are in different currencies, and `null` for a same-currency transfer.

### Errors

| Status | Cause                                                                                                                                                |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid body, the two accounts are the same, an amount isn’t a positive number, or a cross-currency rate implies a >5% residual.                     |
| `403`  | The role lacks the **Bank Accounts** permission, an account isn’t a bank account in this organization, or the date falls on or before the lock date. |
| `404`  | A bank account does not exist in the token’s organization.                                                                                           |

***

## Fetch a transaction

Returns the posted journal entry behind a payment, spend/receive, transfer or manual journal, with every debit/credit line.

```bash
curl https://app.accountfun.com/api/v1/transactions/je-7f3a91 \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

### Response — `200 OK`

```json
{
  "transaction": {
    "id": "je-7f3a91",
    "type": "spend_money",
    "date": "2026-10-08",
    "reference": "STMT-2026-10",
    "description": "Monthly account fee",
    "isReversal": false,
    "reversalOfId": null,
    "isReversed": false,
    "isReconciled": false,
    "contact": { "id": "...", "name": "City Bank" },
    "createdAt": "2026-10-08T09:14:31.002Z",
    "lines": [
      {
        "id": "...",
        "account": { "id": "...", "code": "493", "name": "Bank Charges" },
        "debit": "12.50",
        "credit": "0.00",
        "currency": "USD",
        "exchangeRate": "1",
        "description": "Bank charges",
        "taxRate": null
      },
      {
        "id": "...",
        "account": { "id": "...", "code": "090", "name": "Business Account" },
        "debit": "0.00",
        "credit": "12.50",
        "currency": "USD",
        "exchangeRate": "1",
        "description": null,
        "taxRate": null
      }
    ]
  }
}
```

Access follows the in-app transaction rule — the role must be able to see the entry through its banking, reporting, document, managed-accounts or fixed-asset access. A role that can’t see it gets `403`; an unknown id gets `404`. `isReconciled` is `true` when any line has been matched to a bank statement.
