---
title: "Create a recurring invoice"
method: POST
path: "/recurring-invoices"
tags: ["Recurring Invoices"]
---

# Create a recurring invoice

`POST /recurring-invoices`

## Request

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

The template currency (`meta.currency`) must be one of the supported currencies (stored in canonical form); otherwise 400 UNSUPPORTED_CURRENCY (with `currency`). Each run creates the invoice through the invoice write and its rules.

## Request body

- RecurringInvoiceWrite
  - `client_id` string, uuid, required
  - `interval` 'monthly' | 'quarterly' | 'yearly', required
  - `next_date` string, date, required
  - `template` InvoiceWrite, required
    - `invoice_number` string, nullable: Explicit number (imports, own numbering schemes), stored as given also on a draft; 409 DUPLICATE_INVOICE_NUMBER when another document of the same type in the profile already uses it. If omitted, a draft stays unnumbered and the next number from the profile prefix + counter (e.g. `IV-010`) is assigned when the document is issued (status leaves draft, or it is emailed). null clears the number of a never-issued draft; an issued document keeps its number.
    - `client_id` string, uuid: UUID of an existing client. The API fetches the client data and snapshots it on the invoice. Returns `404` if not found.
    - `status` 'draft' | 'sent'
    - `issue_date` string, date: Defaults to today if omitted.
    - `due_date` string, date: Defaults to `issue_date` + profile payment deadline days (default 30) if omitted.
    - `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.
    - `exchangeRate` number, double, nullable: Foreign-currency invoice only: CHF per 1 unit of `currency`, stored as a manual rate (MWSTV 45 Abs. 3bis). Omit to use the ESTV/BAZG rate of the invoice date. Taken while the invoice is a draft or by the write that issues it; an issued invoice keeps its rate. Issuing a foreign-currency invoice without any rate (e.g. dated in a Monatsmittel month that has not started) is refused with 400 FX_RATE_UNAVAILABLE; when ESTV/BAZG cannot be reached and no rate is cached the write answers 400 FX_RATE_SOURCE_UNAVAILABLE (retry later, it is not a missing rate); 400 INVALID_EXCHANGE_RATE when not a positive number, 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`). `null` is the same as omitting it (unlike the expense and revenue update, it does not drop a stored manual rate).
    - `lines` InvoiceLine[], required
      - `description` string, required
      - `quantity` number, double, required
      - `price` number, double, required
      - `vat_rate` number, double, nullable: VAT rate as a percentage (e.g. 8.1 for 8.1 %). If omitted, the profile's default tax rate is used.
    - `notes` string, nullable
    - `payment_terms` string, nullable
  - `active` boolean

## Response `201`

Recurring invoice created

- RecurringInvoice
  - `id` string, uuid, required
  - `client_id` string, uuid, required
  - `interval` 'monthly' | 'quarterly' | 'yearly', required
  - `next_date` string, date, required: Date on which the next invoice will be generated
  - `template` InvoiceWrite, required
    - `invoice_number` string, nullable: Explicit number (imports, own numbering schemes), stored as given also on a draft; 409 DUPLICATE_INVOICE_NUMBER when another document of the same type in the profile already uses it. If omitted, a draft stays unnumbered and the next number from the profile prefix + counter (e.g. `IV-010`) is assigned when the document is issued (status leaves draft, or it is emailed). null clears the number of a never-issued draft; an issued document keeps its number.
    - `client_id` string, uuid: UUID of an existing client. The API fetches the client data and snapshots it on the invoice. Returns `404` if not found.
    - `status` 'draft' | 'sent'
    - `issue_date` string, date: Defaults to today if omitted.
    - `due_date` string, date: Defaults to `issue_date` + profile payment deadline days (default 30) if omitted.
    - `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.
    - `exchangeRate` number, double, nullable: Foreign-currency invoice only: CHF per 1 unit of `currency`, stored as a manual rate (MWSTV 45 Abs. 3bis). Omit to use the ESTV/BAZG rate of the invoice date. Taken while the invoice is a draft or by the write that issues it; an issued invoice keeps its rate. Issuing a foreign-currency invoice without any rate (e.g. dated in a Monatsmittel month that has not started) is refused with 400 FX_RATE_UNAVAILABLE; when ESTV/BAZG cannot be reached and no rate is cached the write answers 400 FX_RATE_SOURCE_UNAVAILABLE (retry later, it is not a missing rate); 400 INVALID_EXCHANGE_RATE when not a positive number, 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`). `null` is the same as omitting it (unlike the expense and revenue update, it does not drop a stored manual rate).
    - `lines` InvoiceLine[], required
      - `description` string, required
      - `quantity` number, double, required
      - `price` number, double, required
      - `vat_rate` number, double, nullable: VAT rate as a percentage (e.g. 8.1 for 8.1 %). If omitted, the profile's default tax rate is used.
    - `notes` string, nullable
    - `payment_terms` string, nullable
  - `active` boolean, required
  - `created_at` string, date-time, required

## Other responses

- `400`: Validation error
- `401`: Invalid or missing API key
- `403`: Subscription required or read-only key on a write endpoint
- `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)
