# Cash application and remittance matching

Cash application is the process of taking an incoming customer payment from a bank feed or lockbox and applying it against the right open invoices in the general ledger. Apideck's [Accounting API](/apis/accounting/reference) covers the full loop: pull open invoices and credit notes, create a `payment` with `allocations` that splits the receipt across those documents, and let the downstream system close the invoices and surface any residual as unapplied cash. The sections below walk through one-to-one and one-to-many matches, partial payments, overpayments, short-pay write-offs, FX, and safe re-runs.

## One integration for every ledger

AR automation and treasury products need to read open invoices and write cash receipts across every ledger their customers use. The payment allocation payload differs across QuickBooks `Payment`, Xero `Payment`, NetSuite `CustomerPayment`, Sage Intacct `AR Payment`, and Exact Online `Receivable`. Apideck normalizes all of them into one `payments` resource with an `allocations` array.

- One `allocations[].id` + `type` + `amount` shape works on every connector that accepts payments.
- Invoice status (`authorised`, `partially_paid`, `paid`) and `balance` are computed downstream from allocations, so you never write them directly.
- Credit notes allocate through the same array as invoices (`type: credit_memo`), so refunds and short-pay offsets reuse the same code path.
- `pass_through` covers connector-specific fields the unified model does not expose.

## Resource mapping

Payments created through `POST /accounting/payments` map to the following downstream objects:

| Connector | Downstream object |
| --- | --- |
| QuickBooks | `Payment` (AR) |
| Xero | `Payment` applied to `Invoice` |
| NetSuite | `CustomerPayment` |
| Sage Intacct | `AR Payment` (`ARPYMT`) |
| Exact Online | `Receivable` entry on the sales journal |
| Odoo | `account.payment` with reconciliation lines |
| FreshBooks | `Payment` on an invoice |
| Zoho Books | `CustomerPayment` with `invoices` array |
| MYOB | `CustomerPayment` |
| Microsoft Dynamics 365 Business Central | `Cash Receipt Journal` application (read-only via unified API) |
| Workday | `Customer Payment` |

## Walkthrough

The flow has four phases: pull the open AR for the customer, match the remittance to one or more documents, post a payment with `allocations`, and handle residuals.

### 1. Pull open invoices for the paying customer

Start by narrowing the open AR to the customer named on the remittance or inferred from the bank narrative. Resolve the customer first with [Customers list](/apis/accounting/reference#operation/customersAll), filtering on `email` or `company_name`, or reuse a customer id you already mapped.

```http
GET /accounting/customers?filter[company_name]=Northwind%20Traders HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: {CONSUMER_ID}
x-apideck-service-id: xero
```

With the `customer.id`, pull that customer's invoices with [Invoices list](/apis/accounting/reference#operation/invoicesAll) and `filter[customer_id]`. The invoices filter has no status option, so keep only invoices with `balance > 0` and a status other than `paid`, `void`, or `deleted` on your side. Add `filter[updated_since]` on incremental runs to avoid paging through old paid history every time.

```http
GET /accounting/invoices?filter[customer_id]=cust_01HK9W0Y3F8M4R1DQBZN7KTCMV&limit=200 HTTP/1.1
Host: unify.apideck.com
Authorization: Bearer {API_KEY}
x-apideck-app-id: {APP_ID}
x-apideck-consumer-id: {CONSUMER_ID}
x-apideck-service-id: xero
```

If the customer may hold open credits, also pull their credit notes with [Credit notes list](/apis/accounting/reference#operation/creditNotesAll) and `filter[customer_id]`. The credit notes filter has no status option either; keep the ones with `remaining_credit` (or `balance`) above zero.

For high-volume AR, use the aged receivables report from [`GET /accounting/aged-debtors`](/apis/accounting/reference#operation/agedDebtorsOne) to rank customers before you drill into their invoices.

### 2. Parse the remittance

The remittance side is specific to your ingestion pipeline. The matching rules that matter for the Accounting API are:

1. Extract candidate invoice references: invoice numbers, PO numbers, and any display id the customer put on the wire.
2. Capture the paid amount and the posting currency from the bank line.
3. Capture a stable external reference. The bank transaction ID is a good `reference` to carry through to the payment, and it is the key you dedupe on later.

Match the extracted references to the `number`, `po_number`, or `display_id` fields on the invoices you fetched in step 1. Keep the resulting set of `{ invoice_id, invoice_balance, applied_amount }` tuples in memory for the next call.

### 3. Post the payment with allocations

Send one `POST /accounting/payments` per bank receipt. The `allocations` array is where cash application happens: each entry references an invoice (or credit note) `id` and the `amount` to apply. Use [Payments create](/apis/accounting/reference#operation/paymentsAdd).

A one-to-one match looks like this. The `reference` carries the bank transaction ID so the accountant can trace the receipt back to the bank line.

```json
{
  "transaction_date": "2025-03-14T00:00:00.000Z",
  "currency": "USD",
  "total_amount": 1240.00,
  "reference": "bank_txn_8821734",
  "payment_method": "bank_transfer",
  "customer": {
    "id": "cust_01HK9W0Y3F8M4R1DQBZN7KTCMV"
  },
  "accounts_receivable_account_id": "acct_01HKA1R6NM0E7KQ8V3BXYT4JPD",
  "allocations": [
    {
      "id": "inv_01HKA2TPY6V1QX8W4N3RBCKMEF",
      "type": "invoice",
      "amount": 1240.00
    }
  ],
  "type": "accounts_receivable",
  "status": "authorised",
  "note": "Northwind wire - invoice INV-2041"
}
```

Once the payment posts, the downstream system marks the referenced invoice `paid`.

### 4. One payment, many invoices

A single wire often clears several invoices. Add one entry per invoice to `allocations`. The sum of `allocations[].amount` must equal `total_amount` unless you intentionally leave a residual (see step 6).

```json
{
  "transaction_date": "2025-03-14T00:00:00.000Z",
  "currency": "USD",
  "total_amount": 4875.50,
  "reference": "bank_txn_8821740",
  "payment_method": "bank_transfer",
  "customer": {
    "id": "cust_01HK9W0Y3F8M4R1DQBZN7KTCMV"
  },
  "accounts_receivable_account_id": "acct_01HKA1R6NM0E7KQ8V3BXYT4JPD",
  "allocations": [
    { "id": "inv_01HKA2TPY6V1QX8W4N3RBCKMEF", "type": "invoice", "amount": 1240.00 },
    { "id": "inv_01HKA2V8ZK3M5P7T1LWXYQDRHN", "type": "invoice", "amount": 2000.00 },
    { "id": "inv_01HKA2VH4RD9N6WG5B2JXTYPC8", "type": "invoice", "amount": 1635.50 }
  ],
  "type": "accounts_receivable",
  "status": "authorised"
}
```

### 5. Partial payment on one invoice

For partial payments, set `allocations[].amount` to the amount applied rather than the invoice total. The invoice moves to `partially_paid` and its `balance` drops by that amount. The next incoming payment can target the same invoice `id` again.

```json
{
  "transaction_date": "2025-03-14T00:00:00.000Z",
  "currency": "USD",
  "total_amount": 500.00,
  "reference": "bank_txn_8821752",
  "payment_method": "bank_transfer",
  "customer": { "id": "cust_01HK9W0Y3F8M4R1DQBZN7KTCMV" },
  "accounts_receivable_account_id": "acct_01HKA1R6NM0E7KQ8V3BXYT4JPD",
  "allocations": [
    { "id": "inv_01HKA2V8ZK3M5P7T1LWXYQDRHN", "type": "invoice", "amount": 500.00 }
  ],
  "type": "accounts_receivable",
  "status": "authorised"
}
```

### 6. Overpayment and unapplied cash

If the customer wires more than the open invoices, allocate what you can match and leave the balance unapplied. Set `total_amount` to the received cash and allocate only the matched portion. The downstream ledger treats the difference as unapplied cash (QuickBooks `UnappliedCash`, Xero `Overpayment`, NetSuite `Unapplied Payment`, Sage Intacct on-account balance).

```json
{
  "transaction_date": "2025-03-14T00:00:00.000Z",
  "currency": "USD",
  "total_amount": 1500.00,
  "reference": "bank_txn_8821790",
  "customer": { "id": "cust_01HK9W0Y3F8M4R1DQBZN7KTCMV" },
  "accounts_receivable_account_id": "acct_01HKA1R6NM0E7KQ8V3BXYT4JPD",
  "allocations": [
    { "id": "inv_01HKA2TPY6V1QX8W4N3RBCKMEF", "type": "invoice", "amount": 1240.00 }
  ],
  "type": "accounts_receivable",
  "status": "authorised",
  "note": "260.00 unapplied, pending allocation"
}
```

The payment `type` enum also includes `accounts_receivable_overpayment` and `accounts_receivable_prepayment`, and the allocation `type` enum includes `over_payment` and `pre_payment`, for ledgers that model on-account cash as its own document. To clear the residual later, apply it when the next invoice arrives; check how the target connector represents the overpayment in the coverage matrix before automating that step.

### 7. Short payments: credit notes and write-offs

When the customer pays less than invoice face value, you have two options.

Allocate the short-pay to an existing credit note so AR nets to zero. Pull the customer's credit notes with [Credit notes list](/apis/accounting/reference#operation/creditNotesAll), then reference the credit note in the same `allocations` array alongside the invoice, with `type: credit_memo`:

```json
{
  "transaction_date": "2025-03-14T00:00:00.000Z",
  "currency": "USD",
  "total_amount": 1150.00,
  "reference": "bank_txn_8821801",
  "customer": { "id": "cust_01HK9W0Y3F8M4R1DQBZN7KTCMV" },
  "accounts_receivable_account_id": "acct_01HKA1R6NM0E7KQ8V3BXYT4JPD",
  "allocations": [
    { "id": "inv_01HKA2TPY6V1QX8W4N3RBCKMEF", "type": "invoice", "amount": 1240.00 },
    { "id": "cn_01HKA3P9EF2BT6RM0YQWZXD5HJ", "type": "credit_memo", "amount": -90.00 }
  ],
  "type": "accounts_receivable",
  "status": "authorised"
}
```

The sign convention for credit allocations can differ by connector. Test against a sandbox tenant before going live.

If no credit note exists and the short-pay is a write-off (small deductions, early-pay discounts, currency rounding), create a credit note first with [Credit notes create](/apis/accounting/reference#operation/creditNotesAdd) coded to the write-off account, then allocate it the same way. On connectors where credit notes are read-only or not in coverage (Exact Online family, MYOB, Moneybird, Workday, Kashflow, Clearbooks UK, Stripe), the credit note has to exist natively in the downstream system before it can be referenced in `allocations`.

### 8. Payments in a foreign currency

For payments that land in a different currency than the invoice, set `currency` and `currency_rate` on the payment and allocate in the invoice currency. The downstream system books the realized FX gain or loss.

```json
{
  "transaction_date": "2025-03-14T00:00:00.000Z",
  "currency": "EUR",
  "currency_rate": 1.0875,
  "total_amount": 1140.23,
  "reference": "bank_txn_8821815",
  "customer": { "id": "cust_01HK9W0Y3F8M4R1DQBZN7KTCMV" },
  "accounts_receivable_account_id": "acct_01HKA1R6NM0E7KQ8V3BXYT4JPD",
  "allocations": [
    { "id": "inv_01HKA2TPY6V1QX8W4N3RBCKMEF", "type": "invoice", "amount": 1240.00 }
  ],
  "type": "accounts_receivable",
  "status": "authorised"
}
```

For the detailed mechanics of posting cross-currency receipts and reconciling realized gain/loss accounts, see the [FX payments guide](/guides/fx-payments-accounting-api).

### 9. Safe re-runs

Cash application pipelines re-run constantly: webhooks replay, nightly batch jobs backfill, bank files land twice. [Payments create](/apis/accounting/reference#operation/paymentsAdd) does not accept the `Idempotency-Key` header that invoice, bill and journal entry writes support, so build the write path so a re-run cannot double-pay an invoice.

- Keep your own table keyed by the bank transaction ID. Before calling `POST /accounting/payments`, check it; after a successful create, store the returned payment `id` against the bank transaction ID.
- The payments list has no `reference` filter. To reconcile after a crash between the create and your table write, list [Payments](/apis/accounting/reference#operation/paymentsAll) with `filter[customer_id]` (or `filter[invoice_id]`) and `filter[updated_since]`, and compare `reference` on your side.
- If an allocation needs to change (the customer re-remits with corrected invoice numbers), use [Payments delete](/apis/accounting/reference#operation/paymentsDelete) and re-create. Updating allocations in place is not uniformly supported across connectors.
- Do not dedupe on the payment `id`. Downstream systems generate their own IDs; the bank transaction ID is the value you control end to end.

## Connector-specific behavior

Coverage below reflects what `POST /accounting/payments` with `allocations` supports today. "Standard allocations" means the connector accepts the standard `allocations[].{ id, type, amount }` payload and lets the downstream system close the referenced invoice. Credit note allocations are called out separately because some connectors read credit notes but will not accept them as allocation targets on a receipt.

| Connector | Notes |
| --- | --- |
| `access-financials` | Payments and credit notes supported. Standard allocations. |
| `acumatica` | Payments and credit notes supported. Standard allocations. Tracking categories are not in coverage, so omit `tracking_categories` on the payment. |
| `api-explorer` | Sandbox connector for development only. Not a target for real cash application. |
| `banqup` | Invoices are read-only and payments are not in coverage. Use this connector for ingestion only. |
| `campfire` | Payments coverage is limited. Validate behavior for AR cash receipts before relying on it; standard mapping is not guaranteed. |
| `clearbooks-uk` | Invoices and credit notes are read-only; payments are not in coverage. Not a target for creating `payments`. |
| `coverage` | Coverage metadata only. Not a real accounting connector. |
| `digits` | Reporting-only connector. Payments and invoices not in coverage. |
| `dualentry` | Payments and credit notes supported. Standard allocations. |
| `exact-online` | Payments post as `Receivable` entries. Credit notes are read-only, so credit-note allocations must reference existing downstream credit notes rather than ones created through the unified API. |
| `exact-online-nl` | Same behavior as `exact-online`. Credit notes read-only. |
| `exact-online-uk` | Payments supported. Bills and credit notes are read-only. |
| `freeagent` | Payments and credit notes supported. Standard allocations. |
| `freshbooks` | Payments map to `Payment` on an invoice. Standard allocations supported through the unified model. |
| `intuit-enterprise-suite` | Behaves like QuickBooks for AR: payment with multi-line allocations, credit notes supported. Also supports classes, departments, and locations for ledger coding. |
| `kashflow` | Invoices, payments, and credit notes are all read-only. Not a target for creating cash receipts. |
| `microsoft-dynamics-365-business-central` | Payments are read-only through the unified API, so this connector can read reconciled receipts but not post them. Credit notes are supported for read and write and can be created separately. |
| `moneybird` | Payments supported. Credit notes are not in coverage, so handle short-pays as unapplied cash or via a journal entry. |
| `mrisoftware` | Customers and suppliers are read-only. Payments and invoices not in coverage. |
| `myob` | Payments supported. Credit notes are read-only, so credit allocations must reference existing downstream credit notes. |
| `myob-acumatica` | Payments and credit notes supported. Standard allocations. |
| `netsuite` | Payments post as `CustomerPayment`. Allocations map to `apply` lines. Credit notes supported. Use `pass_through` to set the deposit-to account if your customers run multiple undeposited funds accounts. |
| `odoo` | Payments post as `account.payment` with reconciliation lines to the chosen invoices. Credit notes supported. Partial reconciliation is native. |
| `overview` | Metadata connector. Not a real accounting connector. |
| `pennylane` | Invoices and bills supported, but payments are not in coverage. Record cash receipts out-of-band and read them back via invoices. |
| `procountor-fi` | Payments are read-only. Use for reconciliation state, not for writing receipts. |
| `quickbooks` | Payments map to QuickBooks `Payment`. Multi-invoice allocations are native. Credit notes supported and can be allocated against invoices on the same payment. |
| `rillet` | Payments and credit notes supported. Standard allocations. |
| `sage-business-cloud-accounting` | Payments and credit notes supported. Standard allocations. |
| `sage-intacct` | Payments post as `ARPYMT`. Allocations and credit notes supported. For multi-entity setups, pass the posting entity via `pass_through` if the receipt needs to post to a specific location. |
| `sage-intacct-rest` | Same AR model as `sage-intacct`. Credit notes supported. |
| `stripe` | Invoices and credit notes supported, payments are read-only. Treat Stripe as the source of truth for the receipt and sync it elsewhere; do not try to post payments back into Stripe through the unified API. |
| `visma-netvisor` | Payments and credit notes supported. Standard allocations. |
| `wave` | Invoices supported. Payments and credit notes not in coverage. |
| `workday` | Payments supported. Credit notes are not in coverage, so handle short-pays via journal entries. |
| `xero` | Payments allocate against invoices natively. Credit notes supported and allocate as `type: credit_memo` on the same payment. Set `account.id` on the payment to the Xero bank account the receipt lands in. |
| `yuki` | Invoices and credit notes are read-only; payments are not in coverage. Use for reading AR state only. |
| `zoho-books` | Payments map to `CustomerPayment` with an `invoices` array. Credit notes supported. Standard allocations. |

### NetSuite

NetSuite records undeposited funds through a deposit-to account on the payment record. If your implementation needs to vary this per bank account or per subsidiary, send it with `pass_through` on `POST /accounting/payments`. The unified `accounts_receivable_account_id` sets the AR control account, not the deposit account.

### Xero

Xero requires a bank `account.id` on the payment for the receipt to post, in addition to the AR account. If you omit it, the payment fails validation. When you apply a credit note on the same payment, Xero posts the credit allocation as a separate `CreditNoteAllocation` under the hood, while the unified request stays a single `payments` call with one combined `allocations` array.

### Exact Online, Exact Online NL, Exact Online UK

Credit notes are read-only across the Exact family. For short-pay offsets, your remittance pipeline needs to match against pre-existing downstream credit notes rather than creating new ones. The `credit_memo` allocation type still works; you cannot source the credit note via `POST /accounting/credit-notes`.

### QuickBooks and Intuit Enterprise Suite

Both treat the `Payment` as the aggregate and the invoice allocations as `Line.LinkedTxn` entries. Multi-invoice allocations are first-class. For class, department, or location coding on the payment itself, use `tracking_categories`; invoice coding is inherited from the referenced invoice.

### Sage Intacct and Sage Intacct REST

In multi-entity environments, set the posting entity via `pass_through` with `LOCATIONID`. The AR account chosen must belong to the same entity as the invoices being allocated, otherwise the receipt fails Intacct's inter-entity validation.

## Next steps

- [Mark invoices and bills as paid](/guides/mark-invoices-as-paid) covers the base allocation model this guide builds on.
- [FX and multi-currency payments](/guides/fx-payments-accounting-api) for cross-currency receipts and realized gain/loss handling.
- [Aged receivables](/apis/accounting/reference#operation/agedDebtorsOne) to rank customers before pulling their open invoices.
- [Credit notes reference](/apis/accounting/reference#tag/Credit-Notes) for creating write-off credits that can be allocated on a short-pay.
- [Webhooks for accounting events](/apis/webhook/reference) to trigger cash application when a new invoice is created or a payment is reconciled downstream.
