Developer dashboard

Invoice Payments#

Collect, record and track invoice payments, including partial payments and payment history.


Overview#

There are two ways money reaches an invoice:

  • 💳 Customer pays the pay link - card, ACH or a saved card on the hosted pay page (/api/v1/public/invoices/...). The charge and the invoice update happen together.
  • 📝 Merchant records a payment - money already collected elsewhere (portal collect, terminal, cash, check) is recorded against the invoice with /payment or /external-payment. These endpoints never charge anything.

Every payment is appended to the invoice's paymentHistory, and amountPaid, amountDue and status move with it:

  • 📊 Partial payments - only when allowPartialPayment is true (optionally with a minimum payment)
  • ✅ Fully paid - status becomes paid and paidAt is stamped when amountDue reaches 0

Admin endpoints use Authorization: Bearer YOUR_TOKEN + X-Tenant-ID (or x-api-key).


Record Payment#

Record money that has already been collected (full or partial). Do not send card data — this endpoint does not charge.

Endpoint#

POST/api/v1/invoices/:invoiceId/payment

POST /api/v1/invoices/:invoiceId/record-payment is an alias.

Record Paymentbash
curl -X POST https://uat.auxcore.net/api/v1/invoices/0b8e6a3c-5d2f-4c71-9e1a-7f3b2c9d4e10/payment \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 500.00,
    "paymentMethod": "card",
    "transactionId": "TXN-20261001-8F3A2C",
    "notes": "Collected at the counter"
  }'
Parameter Type Required Description
amount number Yes Amount collected
paymentMethod string Yes Free text, e.g. card, ach, cash
transactionId string No The transaction that collected the money
notes string No Shown in the activity log
isExternal boolean No Marks the entry as recorded outside AuxCORE

Response: the updated invoice.

Record Paymentjson
{
  "success": true,
  "data": {
    "id": "0b8e6a3c-5d2f-4c71-9e1a-7f3b2c9d4e10",
    "status": "partial",
    "totalAmount": "1000.00",
    "amountPaid": 500.00,
    "amountDue": 500.00,
    "paymentHistory": [
      {
        "transactionId": "TXN-20261001-8F3A2C",
        "amount": 500.00,
        "paymentMethod": "card",
        "paidAt": "2026-10-01T14:00:00.000Z",
        "notes": "Collected at the counter",
        "isExternal": false,
        "balanceAfter": 500.00,
        "actorId": "a4c2e9d1-7b3f-4e8a-9c21-5d6e7f8a9b0c",
        "actorEmail": "jane@merchant.example"
      }
    ]
  }
}

Validation#

  • The invoice must be sent, viewed, partial or overdue (INV-014); otherwise 400.
  • An amount below amountDue is rejected when allowPartialPayment is false.
  • /payment is a settlement endpoint and does not check the minimum payment rule or the remaining balance — run Payment Preflight first when you decide the amount yourself.

Payment Preflight#

Check an amount against the invoice's rules before charging it elsewhere (e.g. a saved card or ACH collect). Nothing is recorded.

Endpoint#

POST/api/v1/invoices/:invoiceId/payment-preflight
Payment Preflightbash
curl -X POST https://uat.auxcore.net/api/v1/invoices/0b8e6a3c-5d2f-4c71-9e1a-7f3b2c9d4e10/payment-preflight \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 250.00 }'

Response:

Payment Preflightjson
{
  "success": true,
  "data": { "amount": 250.00, "amountDue": 500.00, "minimumPayment": 125.00 }
}

400 VALIDATION_ERROR when the invoice is not collectable, the amount is not greater than zero, exceeds the remaining balance, is partial on a full-payment-only invoice, or is below the minimum payment.


Record External Payment#

Record merchant-reported money received outside AuxCORE.

Endpoint#

POST/api/v1/invoices/:invoiceId/external-payment

Request#

Requires a user token with the invoices.manage permission (API-key calls are rejected with 403).

Requestbash
curl -X POST https://uat.auxcore.net/api/v1/invoices/0b8e6a3c-5d2f-4c71-9e1a-7f3b2c9d4e10/external-payment \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 500.00,
    "paymentMethod": "Check",
    "paymentDate": "2026-10-01",
    "recordedBy": "Jane Doe",
    "reference": "CHK-1024",
    "internalNote": "Deposited to bank"
  }'
Parameter Required Description
amount Yes Greater than 0 and not above the remaining balance
paymentMethod Yes One of the allowed methods below (exact spelling)
paymentDate Yes YYYY-MM-DD
recordedBy No Display label ("Received by")
reference No e.g. check number
internalNote No Internal note

Allowed Payment Methods#

Allowed Payment Methodstext
Cash
ACH
Check
Wire Transfer
External Card
Other

Behavior#

  • Updates amountPaid, amountDue, and invoice status using the existing invoice balance transition.
  • Appends a paymentHistory item: amount, paymentMethod, paidAt, isExternal: true, balanceAfter, recordedBy, optional notes and actor fields.
  • Combines reference and internalNote into notes ("CHK-1024 · Deposited to bank").
  • Stores recordedBy as a display-only label, and stamps actorId / actorEmail from the authenticated token — the request body can never set the audit actor.
  • Stamps paidAt at midday UTC on the given date, so the date renders unchanged in every viewer timezone.
  • Creates a matching transaction record (provider EXT) so the payment appears in transaction reporting. This is best-effort: if it fails, the invoice is still updated.
  • Does not charge anything and sends no notifications, receipts or webhooks.

Validation#

  • Rejects any invoice that is not collectable — only sent, viewed, partial and overdue are accepted (INV-014).
  • Rejects no-balance invoices.
  • Rejects invalid payment methods or dates.
  • Rejects non-finite, zero, negative, or over-balance amounts.
  • Rejects partial amounts when allowPartialPayment is false, unless the invoice is already partial.
  • Enforces the invoice's minimum payment rule.
  • Requires paymentDate in YYYY-MM-DD format and not before the invoice issue date. The future-date bound allows one day past UTC today, so a merchant east of UTC can record the current local date.

Response#

Returns the updated invoice.

Responsejson
{
  "success": true,
  "data": {
    "id": "0b8e6a3c-5d2f-4c71-9e1a-7f3b2c9d4e10",
    "status": "partial",
    "amountPaid": 500.00,
    "amountDue": 1000.00,
    "paymentHistory": [
      {
        "amount": 500.00,
        "paymentMethod": "Check",
        "paidAt": "2026-10-01T12:00:00.000Z",
        "notes": "CHK-1024 · Deposited to bank",
        "isExternal": true,
        "balanceAfter": 1000.00,
        "recordedBy": "Jane Doe",
        "actorId": "a4c2e9d1-7b3f-4e8a-9c21-5d6e7f8a9b0c",
        "actorEmail": "jane@merchant.example"
      }
    ]
  }
}

Payment History#

There is no separate history endpoint. GET /api/v1/invoices/:invoiceId returns paymentHistory (one entry per payment) together with amountPaid, amountDue, status, transactionId (primary transaction) and paidAt.


Payment Status#

Payment progress is reflected in the invoice status:

Status Description
sent / viewed / overdue No payments received yet
partial Some payments received
pending An ACH payment is processing
paid Fully paid

The hosted pay page uses these public endpoints. They need no API key: possession of the invoice's linkId authorises them. A full edit rotates the linkId; the old one returns 410 LINK_ROTATED.

Get Invoice for Payment#

GET/api/v1/public/invoices/link/:linkId

Returns the invoice plus merchant branding, allowedPaymentMethods (from metadata.acceptCards / metadata.acceptAch), hasSavedCard and hasSavedAch. The first open moves a sent invoice to viewed.

GET /api/v1/public/invoices/link/:linkId/fee-preview returns the customer-facing fee for an amount.

Pay by Card#

POST/api/v1/public/invoices/:linkId/pay
Pay by Cardjson
{
  "pciToken": "pci_tok_9f8e7d6c5b4a3210",
  "amount": 250.00,
  "email": "john@example.com",
  "customerName": "John Doe",
  "billingAddress": "100 Main St",
  "billingCity": "Austin",
  "billingState": "TX",
  "billingPostalCode": "78701",
  "billingCountry": "US"
}
  • pciToken (pci_tok_…, from the AuxCORE SDK hosted fields) or cardToken is required. Raw card fields (cardNumber, CardNumber, CVV) are rejected with 400 VALIDATION_ERROR in every environment.
  • Optional last4 / cardBrand are shown on the receipt; when missing they are looked up from the PCI vault.
  • amount is only honoured when the invoice allows partial payments (capped at amountDue, and at least the minimum payment); otherwise the full balance is charged.
  • The invoice must be sent, viewed, partial or overdue, and card payments must be enabled for it.

Response: { "success": true, "data": { "invoice": { ... }, "transaction": { ... } } }

Pay by ACH#

POST/api/v1/public/invoices/:linkId/pay-ach

Body: accountHolderName, routingNumber, accountNumber, accountType, customerType, businessName, email, phone, billing address fields, consentAccepted, eSignature, optional amount, customerName and saveAch. routingNumber must be 9 digits, accountNumber 4–17 digits, accountType checking or savings, customerType personal or business (businessName required for business); consentAccepted and eSignature are required. Field errors return 400 with error.fields. While the ACH transaction is processing the invoice is pending.

Method Path Purpose
POST /api/v1/public/invoices/:linkId/send-receipt Send a receipt: { "channel": "email" | "sms", "recipient": "...", "transactionId": "..." }
PATCH /api/v1/public/invoices/:linkId/next-payment Payer moves the next automatic instalment: { "nextPaymentDate": "YYYY-MM-DD" } (after tomorrow, not after dueDate; only when allowRescheduleNextPayment)
POST /api/v1/public/invoices/:linkId/saved-card/request-otp Start saved-card verification (OTP)
POST /api/v1/public/invoices/:linkId/saved-card/verify-otp Verify the OTP
POST /api/v1/public/invoices/:linkId/saved-card/resend-otp Resend the OTP
POST /api/v1/public/invoices/:linkId/pay-with-saved-card Charge a verified saved card
PUT /api/v1/public/invoices/:linkId/saved-card/:paymentMethodId/set-default Make a saved card the default
DELETE /api/v1/public/invoices/:linkId/saved-card/:paymentMethodId Remove a saved card

The saved-card OTP endpoints are limited to 10 requests per 15 minutes.


Payment Reminders#

POST/api/v1/invoices/:invoiceId/send-reminder

Sends a reminder email now. See Sending & Delivery.

Note: a configurable automatic reminder schedule is not available via API.


Failed Payments#

A declined card payment on the pay link returns 400 with success: false and a message such as "Payment declined: Insufficient funds", and the invoice is left unchanged so the customer can try again. Nothing is recorded in paymentHistory.


Webhooks#

When the invoice has a webhookUrl, a successful pay-link card or ACH payment POSTs invoice.paid to it:

json
{
  "event": "invoice.paid",
  "invoice": { "id": "0b8e6a3c-...", "status": "partial", "amountPaid": 250.00, "amountDue": 250.00 },
  "transaction": { "transactionId": "TXN-20261002-9C1D4E", "amount": 250.00 }
}

The event is also sent for partial payments — check invoice.status. Payments recorded with /payment or /external-payment do not send it.


Best Practices#

✅ DO:#

  • Allow partial payments - Increases payment rate
  • Preflight before charging - when your integration decides the amount
  • Record every off-platform payment with /external-payment
  • Enable reminders - Reduce overdue invoices
  • Offer multiple methods - Card and ACH

❌ DON'T:#

  • Don't send card data to /payment - it records, it does not charge
  • Don't record more than the balance - /payment does not stop you
  • Don't record the same payment twice - pass transactionId

Common Scenarios#

Scenario 1: Payment Plan#

Customer wants to pay a $500 invoice in 5 installments (the invoice needs allowPartialPayment: true):

bash
# Payment 1, after collecting $100
curl -X POST https://uat.auxcore.net/api/v1/invoices/0b8e6a3c-.../payment \
  -H "Authorization: Bearer YOUR_TOKEN" -H "X-Tenant-ID: your-tenant-id" \
  -H "Content-Type: application/json" \
  -d '{"amount": 100.00, "paymentMethod": "card", "transactionId": "TXN-20261001-1A2B3C"}'

# Continue until paid...

Scenario 2: External Payment#

Customer paid by check outside AuxCORE:

bash
curl -X POST https://uat.auxcore.net/api/v1/invoices/0b8e6a3c-.../external-payment \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "X-Tenant-ID: your-tenant-id" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 500.00,
    "paymentMethod": "Check",
    "paymentDate": "2026-10-01",
    "reference": "CHK-12345",
    "internalNote": "Deposited to bank"
  }'

Next Steps#


Need help? Contact support@auxcore.net