---
title: "Record a payment against an invoice"
method: POST
path: "/payments"
tags: ["Payments"]
---

# Record a payment against an invoice

`POST /payments`

## Request

- Base URL: `https://api.lumabill.ch/v1`
- URL: `https://api.lumabill.ch/v1/payments`
- Auth: HTTP bearer

Marks an invoice as paid (fully or partially) and posts the payment journal entry when bookkeeping is on. Undo with `POST /invoices/{id}/unpay`. A foreign-currency invoice is released at its current CHF book value; the difference to the CHF moved (from `baseAmount`, `exchangeRate` or the ESTV/BAZG rate of the payment date) is booked as realized Kursdifferenz and reported in `fx`. Currency errors (400): FX_RATE_UNAVAILABLE, FX_RATE_SOURCE_UNAVAILABLE (ESTV/BAZG unreachable, retry), INVALID_EXCHANGE_RATE, EXCHANGE_RATE_IMPLAUSIBLE, AMOUNT_OUT_OF_RANGE, PAYMENT_ACCOUNT_CURRENCY_MISMATCH, OPEN_ITEM_RATE_MISSING (the invoice has no stored rate, so its open item has no CHF book value: set the invoice rate first), PAYMENT_EXCEEDS_OPEN (more than is still open in the invoice currency; the invoice row is locked for the payment, so a concurrent payment is seen). `zeroSaldoSkipped` reports a zero-saldo realization that was not booked.

## Request body

- PaymentWrite
  - `invoice_id` string, uuid, required
  - `paidDate` string, date, required
  - `paymentAccountId` string, uuid: Chart-of-accounts UUID of the receiving account (e.g. 1020 Bank). Required when bookkeeping is on (simple or double mode). For a foreign-currency invoice it must be in CHF or in the invoice currency (400 PAYMENT_ACCOUNT_CURRENCY_MISMATCH).
  - `amount` number, double, nullable: Partial payment amount in the invoice currency; omit to pay the remaining balance
  - `exchangeRate` number, double, nullable: Foreign-currency invoice only: the rate actually applied, CHF per 1 unit of the invoice currency. Omit to use `baseAmount` or the ESTV/BAZG rate of the payment date (400 FX_RATE_UNAVAILABLE when none exists, 400 INVALID_EXCHANGE_RATE for a non-positive value, 400 EXCHANGE_RATE_IMPLAUSIBLE when outside 0.5x to 2x of the ESTV/BAZG rate of the date (0.0001 to 100 without one; the body carries `officialRate`, `min`, `max`)).
  - `baseAmount` number, double, nullable: Foreign-currency invoice only: the CHF that actually reached the account (bank statement). Wins over `exchangeRate`.
  - `note` string, nullable

## Response `200`

Payment recorded

- PaymentResult
  - `paymentStatus` 'UNPAID' | 'PARTIALLY_PAID' | 'PAID', required
  - `amountPaid` number, double, required
  - `remaining` number, double
  - `paymentJournalEntryId` string, uuid, nullable
  - `fx` PaymentFx, nullable: Present when a payment of a foreign-currency document was converted (absent for base-currency documents and without bookkeeping). The open item is released at its current CHF book value (after any year-end revaluation, pro rata on partial payments); the difference to the CHF actually moved is the realized Kursdifferenz, booked on the FX result account with `fxKind: realized`.
    - `currency` 'CHF' | 'EUR' | 'USD' | 'GBP' | 'JPY' | 'CAD' | 'AUD' | 'SEK' | 'NOK' | 'DKK' | 'HUF': ISO 4217 code of a currency the API converts (ESTV/BAZG rates). The same list applies to every currency field of the API.
    - `fwAmount` number, double: Settled amount in the document currency
    - `exchangeRate` number, double, nullable: CHF per 1 unit of the document currency on the money leg
    - `rateSource` 'ACTUAL' | 'MANUAL' | 'BAZG'
    - `baseAmount` number, double: CHF actually moved through the payment account
    - `releasedBookValue` number, double: CHF book value released from the open item
    - `realizedFx` number, double: Realized exchange difference in CHF (positive = gain)
  - `zeroSaldoSkipped` 'PRIOR_YEAR_NOT_CLOSED' | 'FX_ACCOUNT_MISSING': Present when the payment emptied a foreign-currency account in FW but the residual CHF book value was not realized (zero-saldo rule): `PRIOR_YEAR_NOT_CLOSED` (the previous fiscal year is open, so the account has no Saldovortrag yet; close it and the residual is realized then), `FX_ACCOUNT_MISSING` (no FX result account: set one in the FX settings). The payment itself is booked.
  - `message` string

## Other responses

- `400`: Validation error
- `401`: Invalid or missing API key
- `403`: Subscription required or read-only key on a write endpoint
- `404`: Resource not found
- `429`: Rate limit exceeded
- `500`: Internal server error

---

[API](https://skmtc.dev/lumabill/apis/lumabill-api.md) · [All operations](https://skmtc.dev/lumabill/apis/lumabill-api/llms.txt) · [OpenAPI document](https://skmtc.dev/lumabill/apis/lumabill-api/revisions/e48e7099b545?raw)
