---
title: "Record a payment against an expense"
method: POST
path: "/expenses/{id}/pay"
tags: ["Expenses"]
---

# Record a payment against an expense

`POST /expenses/{id}/pay`

## Request

- Base URL: `https://api.lumabill.ch/v1`
- URL: `https://api.lumabill.ch/v1/expenses/{id}/pay`
- Auth: HTTP bearer

Records a full or partial payment against an expense and posts the payment journal entry from the given payment account (e.g. bank). Omitting `amount` pays the remaining balance. `amount` is the CHF (base-currency) book value, unlike invoice payments where it is in the invoice currency; a foreign-currency expense therefore refuses `amount` with 400 FW_AMOUNT_REQUIRED: send `fwAmount` (the amount paid in the expense currency) instead. The dashboard always does. The same rule applies to POST /revenue/{id}/receive. Currency errors (400): FX_RATE_UNAVAILABLE, FX_RATE_SOURCE_UNAVAILABLE (ESTV/BAZG unreachable, retry), INVALID_EXCHANGE_RATE, EXCHANGE_RATE_IMPLAUSIBLE (`exchangeRate`, or the rate `baseAmount` implies, outside 0.5x to 2x of the ESTV/BAZG rate of the payment date), AMOUNT_OUT_OF_RANGE, FW_AMOUNT_REQUIRED, PAYMENT_ACCOUNT_CURRENCY_MISMATCH, OPEN_ITEM_RATE_MISSING (the expense has no stored rate, so its open item has no CHF book value), PAYMENT_EXCEEDS_OPEN (`fwAmount` above what is still open; the expense row is locked for the payment, so a concurrent payment is seen). `zeroSaldoSkipped` reports a zero-saldo realization that was not booked.

## Request body

- ExpensePaymentWrite: Foreign-currency expense: the paying account must be in CHF or in the expense currency (400 PAYMENT_ACCOUNT_CURRENCY_MISMATCH). Without `baseAmount` or `exchangeRate` the ESTV/BAZG rate of the payment date is used (400 FX_RATE_UNAVAILABLE when none exists). The response adds `fx` (PaymentFx).
  - `paymentAccountId` string, uuid, required: Chart-of-accounts UUID of the paying account (e.g. 1021 Bank)
  - `paidDate` string, date, required
  - `amount` number, double, nullable: Partial payment amount in CHF (the base-currency book value); omit to pay the remaining balance. Base-currency expenses only: a foreign-currency expense refuses it with 400 FW_AMOUNT_REQUIRED, send `fwAmount` there.
  - `fwAmount` number, double, nullable: Foreign-currency expense only - partial amount in the expense currency. The only way to pay part of a foreign-currency expense (`amount` is refused there).
  - `exchangeRate` number, double, nullable: Foreign-currency expense only - the rate actually applied (CHF per 1 unit). Omit to use `baseAmount` or the ESTV/BAZG rate of the payment date
  - `baseAmount` number, double, nullable: Foreign-currency expense only - the CHF that actually left 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)
