Skip to main content

Recording customer payments

A customer payment is an incoming remittance from a shipper/customer applied against their receivable invoices. It has two levels:
  • The payment (pym_…) — the money that arrived: an amount, a currency, a transaction date, and the check/ACH reference it came in on.
  • Its line items (pli_…) — how that money is split across your invoices. Each line item applies an amount to a specific invoice (inv_…) for a specific customer (cus_…).
This page covers creating a payment with its line items, reading it back, and reconciling it against receivables.
This is the accounts-receivable (money in) side. For paying carriers (money out) the resource is bill-payments, which is a separate endpoint family. For the AR concepts behind these records — reconciliation, short-pays, and the like — see Remittances Management and Accounts Receivable Management.
All amounts are integer cents. totalAmount: 1120000 is $11,200.00, not eleven million dollars. This matches every monetary field in the Upwell API.

Create a payment with its line items

The primary flow is a single POST /api/rest/customer_payments that inserts the payment and its line items together, using a nested paymentLineItems.data array:
When line items are nested under the payment like this, omit paymentId on each line item — the parent payment supplies the link. Fields you don’t set come back null.

Required fields

Field reference

Payment (pym_…) Line item (pli_…)
The line-item amounts don’t have to sum to the payment’s totalAmount — a remittance can be partially applied and reconciled later. For the complete column list, see the customer_payments and customer_payment_line_items entries in the API Reference.

Two different status fields

These are not the same field, and the difference matters:
  • Payment status is optional and drawn from PaymentStatusesEnum: PENDING, SUCCEEDED, FAILED, DISPUTED, REFUNDED, PAYMENT_REVERSED — each defined in the Vocabulary. Most payments leave it unset, and an unset status means the payment succeeded, so read it as status ?? SUCCEEDED.
  • Line-item status is a required free-form string, not a fixed enum. In practice it carries reconciliation states like applied, paid, or completed. Don’t assume a closed set of values or a fixed casing — read it as an opaque string and set whatever your workflow uses.

Read payments back

To filter or sort, use POST /api/rest/customer_payments/search with a Hasura-style where:

Create many at once

POST /api/rest/bulk-customer-payments inserts an array of payments in one call. The response is a Hasura mutation envelope — read the created rows from returning:

Update and delete

Line items on their own

If you’d rather attach line items to an existing payment (or manage them independently), use the line-item endpoints directly — GET/POST /api/rest/customer_payment_line_items and .../customer_payment_line_items/{id}. Here, set paymentId explicitly to link the line item to its payment:
POST /api/rest/bulk-customer-payment-line-items bulk-creates several at once, the same { "inputs": [...] } shape as bulk-customer-payments:

Idempotency and deduplication

If you set sourceSystem and sourceSystemId, the pair is unique per tenant — within your organization no two payments can share it, and no two line items can either. Use your own remittance/transaction reference as sourceSystemId so re-posting the same remittance conflicts instead of creating a duplicate — a simple idempotency key for retried imports.
Testing against staging? Use the staging base URL https://staging.api.upwell.com with your staging API key — the paths are identical, and keys are environment-specific. See Environments for how staging access is provisioned.
For the complete request and response schemas, see the customer_payments and customer_payment_line_items entries in the API Reference. To submit carrier invoices (the payables side) instead, see Submitting carrier invoices via API.