Skip to content

Payments and allocation

How is a payment tied to the documents it settles? Xero stores one payment record per invoice. QuickBooks and NetSuite write one payment with many allocation lines. Going from the second shape to the first turns one user action into N payment records, and a Xero batch write can fail item by item.

Concepts
9
Divergences
3
Structural
2

Endpoints

Xero

PUT /Payments, PUT /BatchPayments

One record per invoice, but an array per call; BatchPayments settles many invoices in one call

QBO

POST /payment, /billpayment

Separate entities for AR and AP

NetSuite

POST /record/v1/customerpayment

apply sublist carries the allocations

The same concept in three vocabularies

Read across a row. Where a cell is absent, the concept has nowhere to live in that system — which is a design constraint, not a gap to paper over.

NativeNative
— A field or object exists for it
PartialPartial
— Exists with a caveat that changes what you can build
DerivedDerived
— Not stored — you compute it
AbsentAbsent
— Nothing in the API
Payments and allocation fields compared across Xero, QuickBooks Online and NetSuite
ConceptXeroQBONetSuite
Customer receipt
Native

Payment

Native

Payment

Native

customerpayment

Supplier payment
Partial

Payment

Same object; the invoice type decides the direction

Native

BillPayment

A separate entity, with PayType

Native

vendorpayment

Allocation modelThe shape difference that changes your write path.
Partial

One payment record per invoice

N invoices means N records

Native

Line[] with LinkedTxn

One payment, many allocations

Native

apply sublist

One payment, many applications

Grouping a run
Native

BatchPayment

Groups payments for a single bank withdrawal

Absent

—

No batch object; group by deposit

Native

Payment batch / EFT

Unapplied cashThe reason an aging report disagrees with the ledger's own.
Native

Overpayment and Prepayment

Separate objects with separate endpoints

Native

UnappliedAmt

On the payment; on accrual basis the credit sits in Accounts Receivable. "Unapplied Cash Payment Income" appears only on cash-basis reports

Native

Unapplied on the payment; customerdeposit

Bank account
Native

Account

Must have EnablePaymentsToAccount set

Native

DepositToAccountRef

Or Undeposited Funds

Native

account

Payment method
Absent

Reference

Free text — no method object

Native

PaymentMethodRef

A managed list

Native

paymentmethod

FX gain on settlement
Native

CurrencyRate on the payment

Xero computes the realised gain

Native

ExchangeRate

QBO computes it

Native

exchangerate

Per accounting book

Reversal
Native

Delete the payment

Status becomes DELETED

Native

Void the payment

operation=void

Native

Delete or reverse

Subject to period lock

What actually differs, and what you do about it

A difference stated without a response is trivia. Each of these carries the handling.

Structural

One user action, N payment records, per-item failure

Xero

Settling five invoices with one bank receipt is one payment with five allocations on QBO and NetSuite, and five Payment records on Xero. Xero accepts them in one call — PUT /Payments takes an array, and BatchPayments settles many invoices at once — but with summarizeErrors=false each item succeeds or fails on its own. Three of five succeed and the ledger holds a half-applied receipt that reconciles to nothing.

Do — Write Xero payments through a job with a stable idempotency key, and check the result of every allocation in the response. The payment is not 'recorded' until every allocation succeeds; show a partial state honestly when it does not.

Structural

Unapplied cash is a separate object in Xero and most integrations skip it

Xero

Xero splits it into Overpayment (the customer paid too much against an invoice) and Prepayment (they paid before an invoice existed), each with its own endpoint. Neither appears in /Invoices. An AR figure built from invoice balances alone disagrees with Xero's own aging report by exactly the unapplied amount.

Do — Fetch both endpoints, model them as documents with no lines, and give unapplied cash its own row in the aging table. Tie to the ledger's aging report and surface any variance.

Sharp edge

AP payments are a different entity in QuickBooks

QBO

Payment settles invoices; BillPayment settles bills. They have different shapes, different fields and different endpoints. A single 'payments' sync that reads only Payment silently omits every supplier payment.

Do — The adapter exposes one payments read that fans out to both entities and tags each with its direction.

The same fact, different payloads

Stacked rather than tabbed — a reader comparing two shapes should not have to hold one of them in memory.

Xero — one record per invoice; five invoices means five of these

Xero
{
  "Invoice": { "InvoiceID": "3f2a…" },
  "Account": { "Code": "090" },
  "Date": "2026-09-30",
  "Amount": 1200.00,
  "Reference": "BACS 30 Sep"
}

QBO — one payment, allocations as lines

QBO
{
  "CustomerRef": { "value": "58" },
  "TotalAmt": 3400.00,
  "DepositToAccountRef": { "value": "35" },
  "Line": [
    { "Amount": 1200.00,
      "LinkedTxn": [{ "TxnId": "145", "TxnType": "Invoice" }] },
    { "Amount": 2200.00,
      "LinkedTxn": [{ "TxnId": "147", "TxnType": "Invoice" }] }
  ]
}

NetSuite — apply sublist, unapplied lines left false

NetSuite
{
  "customer": { "id": "58" },
  "trandate": "2026-09-30",
  "account": { "id": "35" },
  "payment": 3400.00,
  "apply": { "items": [
    { "doc": 8811, "apply": true, "amount": 1200.00 },
    { "doc": 8817, "apply": true, "amount": 2200.00 }
  ] }
}

The type the components consume

payments.ts
type Allocation = {
  documentId: string;         // the invoice or bill being settled
  amount: Money;
  /** Xero writes one record per allocation, so the source ref is per-line, not per-payment. */
  source: SourceRef;
};

type Payment = {
  id: string;
  direction: "receipt" | "disbursement";
  partyId?: string;
  date: string;
  bankAccountId: string;
  method?: string;
  total: Money;
  allocations: Allocation[];
  /** Cash that has landed and settles nothing yet. Its own row in aging. */
  unapplied: Money;
  batchId?: string;
  source: SourceRef;
};

What this changes in the interface

The payoff. Every difference above that reaches the surface, and what the component does about it.

Xero allocation writes are not atomic
CashApplication shows per-allocation state during a write, and a failed allocation is a visible, retryable row — never a single red toast over a form the user has to re-enter.
Unapplied cash is real money in a separate object
AgingTable renders an explicit unapplied row and ties the total to the ledger's own aging report, with the variance shown when it exists.
Payment method is free text on Xero
The method filter is capability-driven: a managed list where the source has one, a text search where it does not.

Where the accounting is explained

What consumes this payload

The normalised object above is what the components read. These are the surfaces it feeds, the words for the concepts in it, and the document that explains the accounting.

Compared acrossXero docsQuickBooks Online docsNetSuite docs

Works in your QuickBooks Online or Xero file

Get your books closed every month

Lopifi is building AI bookkeeping that keeps your books closed on US GAAP as transactions happen, with a CPA review every quarter, for $200 a month. We’re not open yet.

$200/month when we open. No card now.