---
title: "Update a recurring invoice"
method: PATCH
path: "/companies/{company}/recurring-invoices/{recurring_invoice}"
tags: ["Recurring Invoices"]
---

# Update a recurring invoice

`PATCH /companies/{company}/recurring-invoices/{recurring_invoice}`

## Request

- Base URL: `https://api.factuarea.com/v1`
- URL: `https://api.factuarea.com/v1/companies/{company}/recurring-invoices/{recurring_invoice}`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

Partially update a recurring invoice template: every field is optional and an omitted one keeps its current value; `lines`, when sent, replaces the whole set of lines. The cadence and lines apply to invoices generated after the update; previously generated invoices are unaffected.

## Path parameters

- `company` string, uuid, required
- `recurring_invoice` string, uuid, required

## Headers

- `Idempotency-Key` string
- `Factuarea-Version` string, date

## Request body

- UpdateRecurringInvoiceRequest: Partial update of a recurring invoice template; only the fields sent overwrite the current value, omitted ones are preserved. Writable fields mirror creation (`client_id`, `series_id`, `name`, `description`, `frequency`, `holiday_handling`, `start_on`, `end_on`, `notes`, `metadata`, `days_before_due`, `max_occurrences`, `email_to`, `send_automatically`, `generation_mode`, `auto_delivery`, `lines[]`). Omitting `generation_mode` keeps the current mode.
  - `client_id` string, uuid
  - `series_id` string, uuid, nullable
  - `price_list_id` string, uuid, nullable
  - `reprice_strategy` 'existing_catalog_lines' | 'future_lines_only'
  - `name` string, nullable
  - `description` string, nullable
  - `frequency` 'daily' | 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'semiannual' | 'yearly'
  - `holiday_handling` string
  - `start_on` string, date
  - `end_on` string, date, nullable
  - `notes` string, nullable
  - `metadata` Metadata, nullable: A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
  - `tags` string[], nullable
  - `custom_fields` object[], nullable
    - `field` string, required
    - `value` string, required
  - `external_id` string, nullable
  - `days_before_due` integer, nullable
  - `max_occurrences` integer, nullable
  - `email_to` string, email, nullable
  - `send_automatically` boolean: Compatibility flag derived from `generation_mode`. Without `generation_mode`, `true` selects `issue_and_send` and `false` selects `draft`. Together with `generation_mode` it must agree with it (`true` only with `issue_and_send`), otherwise the request is rejected with 422. Prefer sending `generation_mode`.
  - `generation_mode` 'draft' | 'issue' | 'issue_and_send' | 'null', nullable: New generation mode of the recurrence: `draft` (generated invoices stay as drafts), `issue` (they are issued without being emailed) or `issue_and_send` (they are issued and emailed to the `auto_delivery` recipients). Any other value is rejected with 422. Optional: when omitted (or `null`) the current mode is kept, unless `send_automatically` is sent on its own and changes whether the recurrence emails its invoices (`true` switches a non-sending mode to `issue_and_send`; `false` switches `issue_and_send` to `draft`). When sent, it takes precedence and `send_automatically` becomes a derived field (`true` only with `issue_and_send`); sending a `send_automatically` (top-level or inside `auto_delivery`) that contradicts it is rejected with 422. Recipients are only required with `issue_and_send`: if the recurrence has an `auto_delivery` configuration, it must keep at least one recipient (422 `auto_delivery_recipients_required` otherwise). Available in every API version.
  - `auto_delivery` object
    - `send_automatically` boolean, nullable: Compatibility flag derived from `generation_mode`. Without `generation_mode`, `true` selects `issue_and_send` and `false` selects `draft`. Together with `generation_mode` it must agree with it (`true` only with `issue_and_send`), otherwise the request is rejected with 422. Prefer sending `generation_mode`.
    - `recipients` string[], nullable
    - `cc` string[], nullable
    - `subject` string, nullable
    - `body` string, nullable
  - `lines` object[]
    - `description` string, nullable
    - `additional_description` string, nullable
    - `quantity` number
    - `unit_price` union: Unit price. Send a JSON number or a decimal string with up to 4 decimals (`"0.0125"`, `"100.0000"`): the string is the exact form the response returns, so a line read from the API can be sent back unchanged. Derived amounts are computed from it and keep 2 decimals.
      - number
      - string
    - `product_id` string, uuid, nullable
    - `variant_id` string, uuid, nullable
    - `presentation_id` string, uuid, nullable
    - `confirmed_base_quantity` number, nullable
    - `tax_rate` number, nullable: VAT rate of the line (0–100). Optional, and it matters whenever `lines` is sent, because a `PUT` replaces the whole set of lines: a line that omits it (or sends `null`) takes the tax it references or the tax assigned to its product and otherwise the DEFAULT TAX of your company for this kind of document, inherited as a whole: its rate, its indirect-tax regime (IVA, IGIC or IPSI) and its AEAT qualification; an exempt or not-subject default also declares its cause (`exemption_reason`) on the line unless the line sends its own. If none of them exists the request is rejected with 422 `missing_required_param`, with `error.param` = `lines.N.tax_rate` and `error.line_index` = N (the zero-based index of the line); a rate already stored on a line is NOT kept if you resend the line without it. A `0` you send is honoured as a real 0 % rate.
    - `retention` number, nullable
    - `surcharge` number, nullable
    - `exemption_reason` 'E1' | 'E2' | 'E3' | 'E4' | 'E5' | 'E6' | 'N1' | 'N2' | 'null', nullable
    - `regime_key` '01' | '02' | '03' | '04' | '05' | '06' | '07' | '08' | '09' | '10' | '11' | '14' | '15' | '17' | '18' | '19' | '20' | 'null', nullable
    - `retention_rate_id` string, uuid, nullable
    - `surcharge_rate_id` string, uuid, nullable
    - `indirect_tax_regime` 'iva' | 'igic' | 'ipsi' | 'null', nullable
    - `configuration_uuid` string, uuid, nullable
    - `counted_quantity` number, nullable
    - `options` object[], nullable
      - `group_uuid` string, uuid, required
      - `value_uuid` string, uuid, required
    - `configuration_signature` string, nullable
    - `configuration_name` string, nullable
    - `price_source` 'price_list' | 'configuration' | 'presentation' | 'variant' | 'product' | 'manual' | 'supplier_offer' | 'pack_snapshot' | 'null', nullable
    - `price_semantics` 'per_base_unit' | 'per_commercial_unit' | 'null', nullable
    - `price_adjustment_total` number, nullable
    - `option_adjustments_absorbed` boolean, nullable

## Response `200`

- object
  - `data` RecurringInvoice, required: A recurring invoice template that auto-generates invoices on a fixed cadence.
    - `id` string, required
    - `object` 'recurring_invoice', required
    - `client` ClientRef, required
      - `id` string, nullable, required
      - `name` string, nullable, required
    - `series` SeriesRef, required
      - `id` string, nullable, required
      - `code` string, nullable, required
    - `status` string, required: Recurring schedule status (active, paused, completed, cancelled).
    - `price_list_id` string, nullable, required: UUID of the price list selected for generated invoices.
    - `price_list_name` string, nullable, required: Price-list name snapshot stored by the recurrence.
    - `frequency` string, required: Cadence (weekly, monthly, quarterly, yearly, etc.).
    - `name` string, required: Descriptive name of the recurrence.
    - `description` string, nullable, required: Free-text description of the recurrence.
    - `notes` string, nullable, required: Internal notes carried over to the generated invoices.
    - `email_to` string, email, nullable, required: Recipient of the automatic dispatch of generated invoices. `null` if not configured.
    - `send_automatically` boolean, required: Compatibility field DERIVED from `generation_mode`: `true` if and only if `generation_mode` is `issue_and_send` (each generated invoice is issued and emailed to `email_to`). On write, without an explicit `generation_mode`, `true` still means `issue_and_send` and `false` means `draft`; when both are sent and contradict each other the request is rejected with 422.
    - `generation_mode` 'draft' | 'issue' | 'issue_and_send', required: What each run of the recurrence does with the invoice it generates: `draft` leaves it as a draft (no definitive number); `issue` issues it (definitive number, VeriFactu record, stock movements) without emailing it, so it stays `is_sent = false`; `issue_and_send` issues it and emails it to the configured recipients, and it is marked as sent once the mail server accepts the email (if the delivery fails the invoice stays issued and not sent). Available in every API version. `send_automatically` is derived from it.
    - `days_before_due` integer, required: Payment term days (Net X) applied to the due date of each generated invoice.
    - `max_occurrences` integer, nullable, required: Maximum number of invoices to generate before completing the recurrence. `null` = no limit.
    - `occurrences_count` integer, required: Number of invoices already generated by this recurrence.
    - `remaining_occurrences` integer, nullable, required: Invoices remaining until `max_occurrences` is reached. `null` when there is no limit.
    - `holiday_handling` string, required: Adjustment policy when the run date falls on a holiday/weekend (e.g. `none`, `next_business_day`, `previous_business_day`).
    - `start_on` string, date, required
    - `end_on` string, date, nullable, required
    - `next_run_at` string, required: When the next invoice will be generated.
    - `last_run_at` string, nullable, required: When the last invoice was generated, if any.
    - `cancelled_at` string, date-time, nullable, required: When the recurrence was cancelled (irreversible terminal state). `null` if not cancelled.
    - `subtotal` string, required
    - `taxes_total` string, required: NET aggregate of the header taxes: `total_vat + total_surcharge - total_retention`. It is the amount that, added to `subtotal`, yields `total` (`total === subtotal + taxes_total`), so it must NOT be combined with `total_retention`: subtracting the withholding again on top of the aggregate produces a false total. It is NOT the VAT figure of the Spanish Modelo 303 - read `total_vat` for that. These amounts are a PREVIEW aggregated from the template lines (the recurrence persists no header totals), rounded per line exactly as the invoice it will issue stores them.
    - `total_vat` string, required: Output VAT (IVA repercutido) the recurrence will accrue: sum of the VAT of its template lines. This is the figure a Spanish Modelo 303 declares, and it is NOT recoverable from `taxes_total`, which nets the withholding out.
    - `total_retention` string, required: Withholding (retencion de IRPF) the recurrence will apply: sum of the withholding of its template lines, as a POSITIVE amount that SUBTRACTS from the total. This is the figure a Spanish Modelo 130/111 declares. It is ALREADY netted out inside `taxes_total`, so do not subtract it again.
    - `total_surcharge` string, required: Equivalence surcharge (recargo de equivalencia) of the template lines. It ADDS to the total exactly like VAT does, and is ALREADY included inside `taxes_total`.
    - `total` string, required: Total the recurrence will invoice. Two equivalent ways to reconstruct it from the published amounts, and only these two: the EXPLICIT one, identical in the six document families - `total = subtotal + total_vat + total_surcharge - total_retention` - or the AGGREGATE one, specific to the sales-side families - `total = subtotal + taxes_total`. NEVER reconstruct it as `subtotal + taxes_total - total_retention`: `taxes_total` ALREADY has the withholding netted out, so that combination subtracts it twice.
    - `currency` string, required
    - `lines` RecurringInvoiceLine[], required
      - `object` 'recurring_invoice_line', required
      - `description` string, nullable, required
      - `additional_description` string, nullable: Optional secondary description rendered below the line concept on generated invoice PDFs.
      - `quantity` number, required
      - `unit_price` string, required: Unit price as a decimal STRING with 4 decimals (e.g. `"100.0000"`), the scale the template accepts: reading a line and sending it back never changes its price.
      - `tax_rate` number, required
      - `retention` number, required: IRPF withholding percentage applied to the line (0–100). Default 0. Its amount is already aggregated into `taxes`.
      - `surcharge` number, required: Equivalence surcharge (recargo de equivalencia) percentage applied to the line (0–100). Default 0. Its amount is already aggregated into `taxes`.
      - `discount_percent` number, required: Discount percentage applied to the line (0-100) BEFORE any tax, exactly as the domain and the invoice this recurrence will issue apply it. `subtotal` is already net of it, so `unit_price` multiplied by `quantity` does NOT equal `subtotal` on a discounted line. Default 0.
      - `subtotal` string, required
      - `taxes` string, required
      - `total` string, required
      - `exemption_reason` string, nullable, required: LIVA exemption / non-subjection cause of the line (subset of the E and N codes). `null` if not informed.
      - `regime_key` string, nullable, required: VeriFactu regime key (`ClaveRegimen`, AEAT list L8.1) of the line. `null` if not informed.
      - `retention_rate_id` string, nullable, required: Public identifier (UUID v7) of the catalog tax applied as IRPF withholding to the line. Opaque: it does not take part in the `taxes`/`total` computation (the formula uses the flat `retention` percentage). `null` if not informed.
      - `surcharge_rate_id` string, nullable, required: Public identifier (UUID v7) of the catalog tax applied as equivalence surcharge to the line. Opaque: it does not take part in the `taxes`/`total` computation (the formula uses the flat `surcharge` percentage). `null` if not informed.
      - `indirect_tax_regime` 'iva' | 'igic' | 'ipsi' | 'null', nullable: Indirect tax regime override of the recurring template line: `iva`/`igic`/`ipsi` when set per-document (precedence override>zone), otherwise `null`. Writable on create/update; propagated to each generated invoice. The template must be homogeneous (a single non-null regime across all lines, 422 otherwise).
      - `product_id` string, nullable, required
      - `product_name` string, nullable, required
      - `item_kind` string, nullable, required
      - `variant_id` string, nullable, required
      - `variant_name` string, nullable, required
      - `presentation_id` string, nullable, required
      - `presentation_name` string, nullable, required
      - `presentation_mode` string, nullable, required
      - `commercial_unit_code` string, nullable, required
      - `base_unit_code` string, nullable, required
      - `conversion_factor` string, nullable, required
      - `base_quantity` string, nullable, required
      - `price_list_id` string, nullable, required
      - `price_list_name` string, nullable, required
      - `price_source` string, nullable, required
      - `price_unit_code` 'C62' | 'KGM' | 'GRM' | 'LTR' | 'MLT' | 'MTR' | 'MTK' | 'HUR' | 'DAY' | 'H87' | 'XPK' | 'XBX' | 'XDS' | 'XBG' | 'XBO' | 'XDJ' | 'XCA' | 'XTB' | 'XCI' | 'XJY' | 'XEN' | 'XCS' | 'null', nullable, required
      - `counted_quantity` string, nullable, required: Pieces the customer asked for on a variable-weight line sold by count (decimal string, scale 4, greater than 0), e.g. `"4.0000"` for four pieces weighing `quantity` kg in total. Informational: the line is charged and the stock is discounted by `quantity` (the real weight), never by the pieces. `null` on any other line. Only accepted on a `variable_measure` presentation whose unit is a count unit.
      - `counted_unit` 'H87' | 'XPK' | 'XBX' | 'XDS' | 'XBG' | 'XBO' | 'XDJ' | 'XCA' | 'XTB' | 'XCI' | 'XJY' | 'XEN' | 'XCS' | 'null', nullable, required: UN/ECE code of the count unit of `counted_quantity` (`H87` piece, `XPK` pack…). `null` exactly when `counted_quantity` is `null`.
      - `configuration` object, nullable, required: Commercial combination frozen on the line, or `null` when the line was not sold or bought through one (legacy product, manual line).
        - `id` string, required
        - `name` string, nullable, required
        - `signature` string, nullable, required
      - `options` object[], required: Configurable options frozen on the line, in printing order. Always an array, `[]` included.
        - `group` object, required
          - `id` string, required
          - `name` string, nullable, required
        - `value` object, required
          - `id` string, required
          - `name` string, nullable, required
        - `price_adjustment` string, nullable, required: Adjustment of the chosen value per commercial unit. `null` means the value does not change the price — NOT the same as adjusting by `0`.
        - `applied` boolean, required: Whether that amount was actually ADDED to the unit price. `false` when an exact price (price-list entry, combination price or a manually typed unit price) already absorbed it.
      - `price_semantics` 'per_base_unit' | 'per_commercial_unit' | 'null', nullable, required: Monetary semantics of the frozen price: `per_base_unit` (converted once by the presentation factor) or `per_commercial_unit` (never converted). `null` when the line carries no catalog price context.
      - `price_adjustment_total` string, nullable, required: Sum of the adjustments of the chosen option values, as a decimal string.
      - `option_adjustments_absorbed` boolean, nullable, required: TRI-STATE, and `null` is NOT `false`. `true`: the frozen price already includes the option adjustments. `false`: they were added on top. `null`: it could not be determined.
    - `metadata` Metadata, nullable, required: A free map of up to 50 key→value pairs for storing arbitrary structured data (values are strings up to 500 characters). Unlike `custom_fields` — an ordered list of typed `{field, value}` pairs with display semantics, present on the six document resources — `metadata` is an unordered map for opaque integration data; a document may carry both. The master resources (Client, Supplier) have no `custom_fields`, so their `metadata` doubles as the custom-fields store. **Reserved keys (read-only).** When the system auto-issues an invoice from a payment correlation (Stripe/GoCardless/MONEI), it writes `stripe_subscription_id`, `stripe_invoice_id`, `billing_reason`, `period_start` and `period_end` into that invoice metadata automatically. Do not set or overwrite them by hand — the platform owns them and a manual value may be replaced when the correlation runs.
    - `external_id` string, nullable, required: External integration key (ERP/CRM/e-commerce) mapping this document to a record in a third-party system. Free-format, unique per company, filterable via `?external_id=`. `null` when not set. Persistent synchronization key, independent of the request-level `Idempotency-Key`.
    - `tags` string[], required: Free classification tags (lowercase slugs `[a-z0-9-]`, ≤ 40 chars each, ≤ 30 tags). Filterable via `?tags[in]=tag1,tag2` (JSON_CONTAINS, OR semantics). Empty `[]` when there are none.
    - `custom_fields` CustomField[], required: Ordered list of typed custom fields `[{field, value}]` (≤ 50). Distinct from `metadata` (a free key→value map): use `custom_fields` for structured, display-oriented integration metadata. Empty `[]` when there are none.
      - `field` string, required: Field key (non-empty, ≤ 60 chars).
      - `value` string, required: Field value (≤ 500 chars).
    - `auto_delivery` object, required: Rich auto-delivery configuration (superset of the scalar `email_to`): the generated invoices are emailed to `recipients` (with optional `cc`) using `subject`/`body`. `recipients`/`cc` are empty lists and `subject`/`body` are `null` when nothing is configured.
      - `send_automatically` boolean, required: Compatibility field DERIVED from `generation_mode`: `true` if and only if `generation_mode` is `issue_and_send`, in which case each generated invoice is emailed to `recipients`.
      - `recipients` string[], required: Primary recipients of the automatic dispatch. Empty list if not configured.
      - `cc` string[], required: Carbon-copy recipients. Empty list if not configured.
      - `subject` string, nullable, required: Custom email subject. `null` to use the default.
      - `body` string, nullable, required: Custom email body. `null` to use the default.
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required

## Other responses

- `400`: `parameter_unknown` with `subcode` `retired_active_profile_header`: the retired `X-Active-Profile` header was sent; the company goes in the path. `parameter_invalid_format` on `Factuarea-Version`: the header is not a `YYYY-MM-DD` date. `unsupported_api_version`: the `Factuarea-Version` date is not a supported version.
- `401`: Missing or invalid API key.
- `403`: The request is authenticated but not allowed: the API key lacks the required scope (`insufficient_scope`), the plan does not include the public API or this feature (`addon_not_active`, `feature_not_available_in_plan`), or the module is not available with a test key (`module_not_available_in_sandbox`).
- `404`: The requested resource does not exist or belongs to another company.
- `409`: The request conflicts with data that already exists or with a request running at the same time. `resource_already_exists` with `subcode` `external_id_already_exists`: another recurring invoice of the company already uses that `external_id`; `details.existing_resource_id` is its identifier.
- `422`: Validation failed, or the recurring invoice cannot undergo the requested state transition (e.g. resuming a recurrence that is not paused). The `error.param` field identifies which input is invalid, if any.
- `429`: Rate limit exceeded. Retry after the duration in `Retry-After`.
- `500`: Unexpected server error.

---

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