# Payments

> Record customer receipts and supplier payments, reverse them, and attach remittance files through the AccountFUN public API.

Source: https://www.accountfun.com/docs/api/payments/

The payments endpoints let your integration record money that settles invoices and bills — a customer receipt (`receivable`) or a supplier payment (`payable`) — attach the remittance advice, add a note, and reverse a payment that was recorded by mistake.

Reading requires the `payments:read` scope; recording, reversing, attaching files and adding notes require `payments:write`. Unlike the plain read/write scopes, **`payments:write` is an opt-in**: the consent screen lists it under **Only if you tick it:**, and the authorizing user’s role must have the **Record Payments** permission. Reversing also needs the **Void Documents** permission. See [Authentication](https://www.accountfun.com/docs/api/authentication/).

Payments follow the side of the document they settle. A **receivable** payment is sales (the role must see sales documents); a **payable** payment is purchases (the role must see purchase documents). A token whose role sees neither gets `403` on every payments call, and a list returns only the directions the role can see.

Record a payment only once

Each `POST /api/v1/payments` moves real money in the books. Send an [`Idempotency-Key`](#retrying-a-payment-safely) so a timeout or retry can’t record the same payment twice.

## Endpoints

| Method | Path                                 | Scope            | Description                                                        |
| ------ | ------------------------------------ | ---------------- | ------------------------------------------------------------------ |
| `GET`  | `/api/v1/payments`                   | `payments:read`  | List payments (paginated).                                         |
| `POST` | `/api/v1/payments`                   | `payments:write` | Record a customer receipt or supplier payment.                     |
| `GET`  | `/api/v1/payments/{id}`              | `payments:read`  | Fetch one payment with its allocations.                            |
| `POST` | `/api/v1/payments/{id}/reverse`      | `payments:write` | Reverse a payment and restore the documents it settled.            |
| `GET`  | `/api/v1/payments/{id}/attachments`  | `payments:read`  | List the payment’s attached files.                                 |
| `POST` | `/api/v1/payments/{id}/attachments`  | `payments:write` | Attach files to the payment (multipart or JSON base64).            |
| `POST` | `/api/v1/payments/{id}/upload-links` | `payments:write` | Mint a 15-minute, upload-only link for someone who holds the file. |
| `GET`  | `/api/v1/payments/{id}/notes`        | `payments:read`  | List the payment’s History & Notes.                                |
| `POST` | `/api/v1/payments/{id}/notes`        | `payments:write` | Add a note to the payment’s History & Notes.                       |

Related lookups you will usually need: [contacts](https://www.accountfun.com/docs/api/contacts/) (the customer or supplier), the [chart of accounts](https://www.accountfun.com/docs/api/chart-of-accounts/) (the bank account) and the documents being settled — [invoices](https://www.accountfun.com/docs/api/invoices/) or [bills](https://www.accountfun.com/docs/api/bills/). AI assistants can use all of this through the [MCP server](https://www.accountfun.com/docs/api/mcp/).

***

## List payments

Returns a paginated list of payments for the token’s organization, newest first. Only the directions the caller’s role can see are returned. The list includes summary fields and the contact — **not** the per-invoice allocations — so fetch a single payment to see what it settled.

### Request

```bash
curl "https://app.accountfun.com/api/v1/payments?paymentType=receivable&limit=25" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

### Query parameters

| Parameter     | Type    | Default | Description                                                        |
| ------------- | ------- | ------- | ------------------------------------------------------------------ |
| `paymentType` | string  | —       | `receivable` (customer receipts) or `payable` (supplier payments). |
| `contactId`   | string  | —       | Filter to payments for a specific contact (a contact id).          |
| `page`        | integer | `1`     | Page number (1-based, max `10000`).                                |
| `limit`       | integer | `25`    | Page size (max `200`).                                             |

### Response — `200 OK`

```json
{
  "payments": [
    {
      "id": "9d2f7c1e-...",
      "paymentType": "receivable",
      "date": "2026-10-08",
      "amount": "980.00",
      "currency": "USD",
      "exchangeRate": "1",
      "reference": "BANK-REF-4471",
      "contact": {
        "id": "00000000-0000-0000-0000-000000000001",
        "name": "Northwind Traders"
      },
      "bankAccountId": "00000000-0000-0000-0000-00000000000b",
      "isReversed": false,
      "createdAt": "2026-10-08T09:14:31.002Z"
    }
  ],
  "pagination": { "page": 1, "limit": 25, "total": 1, "totalPages": 1 }
}
```

| Field                      | Description                                                                     |
| -------------------------- | ------------------------------------------------------------------------------- |
| `payments[].paymentType`   | `receivable` (money in from a customer) or `payable` (money out to a supplier). |
| `payments[].amount`        | Payment total, in `currency`.                                                   |
| `payments[].isReversed`    | `true` once the payment has been reversed.                                      |
| `payments[].bankAccountId` | The bank account the money moved through.                                       |

***

## Record a payment

Records a customer receipt against one or more invoices, or a supplier payment against one or more bills, and posts it to the ledger immediately — this is the same action as **Record Payment** inside AccountFUN, not a draft.

An allocation can’t exceed the document’s outstanding balance (`400`). For money paid **before** the invoice or bill exists, use a [prepayment](https://www.accountfun.com/docs/api/prepayments/) instead.

### Request

```bash
curl -X POST https://app.accountfun.com/api/v1/payments \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: receipt-inv-101" \
  -d '{
    "paymentType": "receivable",
    "contactId": "00000000-0000-0000-0000-000000000001",
    "bankAccountId": "00000000-0000-0000-0000-00000000000b",
    "date": "2026-10-08",
    "amount": "980.00",
    "reference": "BANK-REF-4471",
    "allocations": [
      { "invoiceId": "1a2b3c4d-...", "amount": "490.00" },
      { "invoiceId": "5e6f7a8b-...", "amount": "490.00" }
    ]
  }'
```

### Body parameters

| Field              | Type                | Required | Description                                                                               |
| ------------------ | ------------------- | -------- | ----------------------------------------------------------------------------------------- |
| `paymentType`      | string              | Yes      | `receivable` (allocations use `invoiceId`) or `payable` (allocations use `billId`).       |
| `contactId`        | string              | Yes      | The customer (receivable) or supplier (payable). Must belong to the token’s organization. |
| `bankAccountId`    | string              | Yes      | The bank account that received or sent the money.                                         |
| `date`             | date (`YYYY-MM-DD`) | Yes      | The payment date.                                                                         |
| `amount`           | string              | Yes      | The total payment amount, in `currency`.                                                  |
| `allocations`      | array               | Yes      | One or more documents this payment settles (see below).                                   |
| `currency`         | string              | No       | 3-letter ISO 4217 code. Defaults to the documents’ currency, or the base currency.        |
| `exchangeRate`     | string              | No       | Rate from the payment currency to the base currency. Omit for the system rate.            |
| `bankExchangeRate` | string              | No       | Bank-to-base rate when the bank account is in a third currency. Omit for the system rate. |
| `reference`        | string              | No       | Free-text reference, e.g. the bank’s reference.                                           |

### Allocation parameters

| Field       | Type   | Required        | Description                                                                                   |
| ----------- | ------ | --------------- | --------------------------------------------------------------------------------------------- |
| `invoiceId` | string | receivable only | The invoice this share of the payment settles.                                                |
| `billId`    | string | payable only    | The bill this share of the payment settles.                                                   |
| `amount`    | string | Yes             | Amount applied to the document, **in the document’s currency**. Can’t exceed its balance due. |

All money values are **strings** (`"980.00"`, not `980`), like every other v1 endpoint.

### Response — `201 Created`

```json
{
  "payment": {
    "id": "9d2f7c1e-...",
    "paymentType": "receivable",
    "date": "2026-10-08",
    "amount": "980.00",
    "currency": "USD",
    "exchangeRate": "1",
    "reference": "BANK-REF-4471",
    "contact": { "id": "...", "name": "Northwind Traders" },
    "bankAccount": { "id": "...", "code": "090", "name": "Business Account" },
    "allocations": [
      { "id": "...", "amount": "490.00", "invoice": { "id": "...", "invoiceNumber": "INV-0101" }, "bill": null },
      { "id": "...", "amount": "490.00", "invoice": { "id": "...", "invoiceNumber": "INV-0102" }, "bill": null }
    ],
    "isReversed": false,
    "createdAt": "2026-10-08T09:14:31.002Z",
    "updatedAt": "2026-10-08T09:14:31.002Z"
  }
}
```

Settled documents move to `PAID` when their balance reaches zero, and their `amountPaid` / `balanceDue` update immediately.

### Errors

| Status | Cause                                                                                              |
| ------ | -------------------------------------------------------------------------------------------------- |
| `400`  | Invalid body — the `details` object names each bad field.                                          |
| `400`  | An allocation exceeds the document’s balance, or a document can’t be settled in its current state. |
| `403`  | The role can’t see the payment’s document area (sales for receivable, purchases for payable).      |
| `403`  | The contact, bank account or a document does not belong to this organization.                      |
| `403`  | The date falls on or before the organization’s lock date.                                          |
| `404`  | A referenced invoice or bill does not exist in the token’s organization.                           |

***

## Retrying a payment safely

`POST /api/v1/payments` supports the `Idempotency-Key` header. Send a unique key per logical payment (up to 255 characters from `A–Z a–z 0–9 . _ -`) and a retry with the same key and body **replays the original response** instead of recording a second payment.

* A repeat with the same key but a **different body** returns `409` (`idempotency_fingerprint_mismatch`) — use a new key for a new payment.
* A repeat while the first request is still running returns `409` (`idempotency_in_flight`) — retry shortly.
* The key and its response are kept for 24 hours. A `5xx` is never stored, so a failed request can be retried with the same key.

See [Idempotency and retries](https://www.accountfun.com/docs/api/errors-and-limits/#idempotency-and-retries) for the shared rules.

***

## Fetch a single payment

```bash
curl https://app.accountfun.com/api/v1/payments/9d2f7c1e-... \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

Returns `{ "payment": { … } }` in the full shape above: the contact, bank account and each allocation, with the invoice or bill it settled. A payment in another organization — or an id that isn’t a valid id — returns `404`.

***

## Reverse a payment

Posts the mirrored reversal, restores the balance of every invoice or bill the payment settled, and marks the payment reversed. This is the same action as reversing a payment inside AccountFUN, and needs the `payments:write` scope plus the live **Void Documents** permission.

```plaintext
POST /api/v1/payments/{id}/reverse
```

No body.

```bash
curl -X POST https://app.accountfun.com/api/v1/payments/9d2f7c1e-.../reverse \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

### Response — `200 OK`

```json
{ "reversalEntryId": "je-abc123", "paymentId": "9d2f7c1e-..." }
```

### Errors

| Status | Cause                                                                                                                                   |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | The payment can’t be reversed in its current state.                                                                                     |
| `403`  | Missing the `payments:write` scope, the `record_payment` permission, or the `void` permission.                                          |
| `404`  | No payment with that id in the token’s organization.                                                                                    |
| `409`  | The payment is already reversed, reconciled to a bank statement, part of a batch, or was collected/refunded through a payment provider. |

Reconciled payments must be unreconciled first

Reconciliation isn’t available through the API. If a payment is reconciled, undo its match on the bank reconciliation page in AccountFUN, then reverse it here.

***

## Attach files

Attach one or more files to a payment at any time — typically the remittance advice or the bank’s confirmation. The same rules as [bill attachments](https://www.accountfun.com/docs/api/bills/#attach-receipts) apply:

* **File types:** PDF, PNG, JPEG, XLSX, XLS, DOCX, DOC, CSV; the content must match the extension.
* **Limits:** 5 MB per file, 25 MB per request, 10 attachments per payment, and the organization’s storage quota.
* Attachments follow the shared [file processing rules](https://www.accountfun.com/docs/guides/files/#upload-security-and-processing): documents are antivirus-checked and can remain pending until cleared; images are resized and re-encoded as JPEG with metadata removed.

### Multipart upload

```bash
curl -X POST https://app.accountfun.com/api/v1/payments/9d2f7c1e-.../attachments \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -F "file=@remittance.pdf"
```

### JSON upload (base64)

```bash
curl -X POST https://app.accountfun.com/api/v1/payments/9d2f7c1e-.../attachments \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"files\": [{\"fileName\": \"remittance.pdf\", \"contentBase64\": \"$(base64 -w0 remittance.pdf)\"}]}"
```

| Field                   | Type   | Description                                                              |
| ----------------------- | ------ | ------------------------------------------------------------------------ |
| `files[].fileName`      | string | File name **with its extension** — the extension decides the file type.  |
| `files[].contentBase64` | string | The file’s bytes, base64-encoded. A `data:…;base64,` prefix is accepted. |

`201` returns `{ "attachments": [ … ] }` with each file’s `id`, `originalName`, `mimeType`, `fileSize`, `createdAt` — and a `warnings` array when some files were rejected. `400` if no file was stored or the payment already has 10; `403` for a view-only role; `404` for an unknown payment; `413` over the size limit or when the storage quota is full; `503` while virus scanning is unavailable.

### List attachments

```bash
curl https://app.accountfun.com/api/v1/payments/9d2f7c1e-.../attachments \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

Returns `{ "attachments": [ … ] }`, oldest first, with each file’s `id`, `originalName`, `mimeType`, `fileSize`, `scanStatus` and `createdAt`.

### Upload links

When the caller can’t send the file itself — an AI assistant in a chat has seen the remittance but doesn’t hold the original — mint a short-lived link and hand it to whoever has the file:

```bash
curl -X POST https://app.accountfun.com/api/v1/payments/9d2f7c1e-.../upload-links \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

No body. Response — `201 Created`:

```json
{
  "uploadLink": {
    "url": "https://app.accountfun.com/upload/9f2c…",
    "uploadUrl": "https://app.accountfun.com/upload/9f2c…/files",
    "expiresAt": "2026-10-08T10:15:00.000Z",
    "maxFiles": 5
  }
}
```

The link works for **15 minutes**, takes up to **5 files**, can only **add** files to that one payment, and needs no sign-in. `url` opens a page where a person picks the file; `uploadUrl` takes the files directly as `multipart/form-data` with no `Authorization` header (`curl -F "file=@remittance.pdf" https://app.accountfun.com/upload/9f2c…/files`). It stops working when the app is disconnected or loses `payments:write`, or when its creator can no longer change the payment. See [bill upload links](https://www.accountfun.com/docs/api/bills/#upload-links) for the full lifecycle.

***

## Notes

Read and add notes in the payment’s **History & Notes**:

```bash
curl -X POST https://app.accountfun.com/api/v1/payments/9d2f7c1e-.../notes \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"body": "Received in full; remittance advice attached."}'
```

`201` returns `{ "note": { "id", "body", "authorId", "viaApp", "createdAt" } }`. `GET` lists the notes newest first. `viaApp` names the app that wrote a note, or is `null` for one typed in AccountFUN. Notes are up to 2000 characters.

## History and audit trail

Everything your integration changes is recorded as the user who authorized it, **via** your app, with the time and where the request came from (IP address and user agent). Your app’s name is the one registered under **API & Developers**. This covers payments recorded and reversed — the ledger entry names your app too — files attached, upload links used, and notes added. These entries appear in the payment’s **History & Notes** (shown as “Jane Lee — via Your App (API)”) and in the organization’s **Audit History**. Every API request is also logged, and the logs are kept for 30 days.
