FinzBooksDevelopers

Payments

Unified resource for customer receipts (flow: INCOMING) and vendor payments (flow: OUTGOING). Create the payment and allocate it to invoices / bills in a single call, or split the work across two calls if you prefer.

Resource shape

{
  "payment_id":   "e8bf7b71-7437-4248-9b3e-cbb4ec48e945",
  "flow":         "INCOMING",            // INCOMING | OUTGOING
  "payment_type": "REGULAR",             // REGULAR | ADVANCE | REFUND
  "contact_id":   "50989551-…",
  "contact_name": "Ice Tales Foods Pvt Ltd",
  "date":         "2026-05-19T00:00:00",
  "amount":       11800.00,
  "mode":         "BANK_TRANSFER",       // CASH | BANK_TRANSFER | CHEQUE | UPI | CARD | OTHER
  "reference_number": "UTR-25051209",
  "description":  null,
  "currency_code":"INR",
  "exchange_rate":1.0,
  "bank_charges": 0.0,
  "tds_amount":   0.0,

  "allocations":  [
    {
      "allocation_id": "3052b92b-…",
      "invoice_id":    "72946802-…",     // null for OUTGOING allocations
      "bill_id":       null,             // null for INCOMING allocations
      "amount":        11800.00
    }
  ],
  "created_time": "...",
  "last_modified_time": "..."
}
GET/paymentsList payments (cursor-paginated)AIBooks.payments.READ

Filter by ?flow=INCOMING or ?flow=OUTGOING to keep AR and AP traffic separate. Standard cursor pagination via ?cursor= and ?per_page=.

GET/payments/{payment_id}Get one payment with allocationsAIBooks.payments.READ

Create — one-shot (recommended)

POST/paymentsRecord a payment and apply it in the same callAIBooks.payments.CREATE

Pass allocations[] to record the payment AND apply it to one or more invoices / bills in a single transaction. The journal entry posts immediately and the target document statuses update.

Required fields

FieldNotes
flowINCOMING for customer receipts, OUTGOING for vendor payments.
contact_idCustomer (INCOMING) or vendor (OUTGOING) id.
datePosting date, ISO 8601.
amountTotal payment amount in currency_code.
modeCASH | BANK_TRANSFER | CHEQUE | UPI | CARD | OTHER. Default BANK_TRANSFER.

Optional fields

FieldNotes
reference_numberUTR / cheque number / external reference.
descriptionFree-text note shown on the journal entry.
payment_typeREGULAR (default) | ADVANCE | REFUND.
bank_account_idBank/Cash account that receives (INCOMING) or pays out (OUTGOING). Get the id from GET /accounts filtered to account_type in BANK/CASH. Omit on single-bank orgs (auto-picks the first BANK/CASH account).
currency_codeISO 4217. Default INR.
exchange_rateFX rate to base currency. Required when currency_code != INR.
bank_chargesForex / wire fees withheld by the bank.
tds_amountTDS withheld by the counterparty.
allocations[]One-shot allocations (see below). Omit to leave the payment unapplied / record an advance.

Allocations array

Each entry must have exactly one of invoice_id (for INCOMING) or bill_id (for OUTGOING), plus an amount. The sum of allocation amounts may not exceed the payment amount.

Example — customer receipt against one invoice

POST /api/public/v1/payments
Authorization: Bearer aibk_pat_…
Content-Type: application/json

{
  "flow":             "INCOMING",
  "contact_id":       "50989551-…",
  "date":             "2026-05-19",
  "amount":           11800,
  "mode":             "BANK_TRANSFER",
  "bank_account_id":  "177b15de-…",   // HDFC current a/c
  "reference_number": "UTR-25051209",
  "allocations": [
    { "invoice_id": "72946802-…", "amount": 11800 }
  ]
}

Example — split across two invoices

{
  "flow":       "INCOMING",
  "contact_id": "50989551-…",
  "date":       "2026-05-19",
  "amount":     15000,
  "mode":       "BANK_TRANSFER",
  "allocations": [
    { "invoice_id": "<INV-A>", "amount": 11800 },
    { "invoice_id": "<INV-B>", "amount":  3200 }
  ]
}

Example — vendor payment against a bill

{
  "flow":       "OUTGOING",
  "contact_id": "<vendor-id>",
  "date":       "2026-05-19",
  "amount":     50000,
  "mode":       "BANK_TRANSFER",
  "allocations": [
    { "bill_id": "<bill-id>", "amount": 50000 }
  ]
}

Validation

  • Sum of allocations[].amount > amount400 validation.invalid_value
  • INCOMING with a bill_id in any allocation → 400
  • OUTGOING with an invoice_id in any allocation → 400
  • Allocation amount > target's outstanding balance → 400
  • Contact / invoice / bill not in this org → 404

Create — two-step (advance receipts)

Omit allocations to record an unapplied receipt / advance. Apply it later — possibly across multiple invoices over time:

POST/payments/{payment_id}/applyApply unapplied amount to one invoice or billAIBooks.payments.UPDATE
POST /api/public/v1/payments/<payment_id>/apply

{
  "invoice_id": "<invoice-id>",
  "amount":     11800
}

The apply endpoint takes one allocation per call. Call it repeatedly to split a receipt across multiple invoices/bills.


Journal entries

Journal entry posting is automatic and runs in the same transaction as the payment header:

  • INCOMING: Dr Bank/Cash / Cr Accounts Receivable
  • OUTGOING: Dr Accounts Payable / Cr Bank/Cash
  • If tds_amount > 0 on INCOMING: Dr TDS Receivable for the withheld portion.
  • If bank_charges > 0: Dr Bank Charges (expense).
  • FX: an exchange gain/loss line is posted when the payment rate differs from the invoice/bill rate.

Webhooks emitted

  • PAYMENT_RECEIVED on INCOMING create.
  • PAYMENT_MADE on OUTGOING create.
  • INVOICE_PAID when an allocation clears the invoice balance to zero.
  • BILL_PAID when an allocation clears the bill balance to zero.

Update

PUT/payments/{payment_id}Update a payment / receiptAIBooks.payments.UPDATE

Update a payment's fields and re-post its journal entry in one call. Every body field is optional— only what you send is changed; the rest is left as-is. The journal entry is regenerated with the correct poster for the payment's direction (receipt vs vendor payment).

Updatable fields

FieldNotes
flowINCOMING | OUTGOING. Direction used when re-posting the journal. Inferred from the payment's allocations when omitted — set it explicitly for an unallocated payment whose direction you're changing.
contact_idMove the payment to a different customer/vendor. Currency re-locks to the new contact.
datePosting date, ISO 8601.
amountNew amount (> 0). Blocked with 409 if the payment is already applied — see below.
modeCASH | BANK_TRANSFER | CHEQUE | UPI | CARD | OTHER.
reference_numberUTR / cheque number / external reference.
descriptionFree-text note on the journal entry.
bank_account_idMove the receipt/payment to a different BANK/CASH account; the journal's bank leg re-posts to it.
currency_code / exchange_rate / bank_chargesFX + fee fields, same semantics as create.

Example — correct the amount and reference of an unapplied receipt

PUT /api/public/v1/payments/<payment_id>
Authorization: Bearer aibk_pat_…
Content-Type: application/json

{
  "amount":           15000,
  "reference_number": "UTR-25051299",
  "description":      "revised per remittance advice"
}

Returns the full updated payment object (same shape as GET /payments/{id}), wrapped in the payment envelope.

Applied-payment guard

Changing amount while the payment is allocated to one or more invoices/bills returns:

409 Conflict
{
  "code":    "conflict.allocated",
  "message": "This payment is applied to invoices/bills. Unapply it before changing the amount, then re-apply.",
  "field":   "amount"
}

To re-amount an applied payment: delete its allocations (or the whole payment and recreate), update, then re-apply. Non-amount fields (date, reference, description, bank account, …) can still be updated while allocated.

Validation

  • amount change on an allocated payment → 409 conflict.allocated
  • bank_account_id not a BANK/CASH account in this org → 404 not_found.resource
  • Payment not in this org → 404

DELETE/payments/{payment_id}Delete (reverses journal + allocations)AIBooks.payments.DELETE

Deleting a payment reverses the bank + AR/AP journal entries and restores the invoice / bill balance. Applied allocations are released and the target document status reverts to SENT / OPEN / PARTIALLY_PAID as appropriate.