---
title: "Update a customer invoice"
method: PUT
path: "/api/external/v2/customer_invoices/{id}"
tags: ["Customer Invoices"]
---

# Update a customer invoice

`PUT /api/external/v2/customer_invoices/{id}`

## Request

- Base URL: `https://app.pennylane.com`
- URL: `https://app.pennylane.com/api/external/v2/customer_invoices/{id}`
- Auth: OAuth 2
  - Authorization code: `https://app.pennylane.com/oauth/authorize`
  - Token URL: `https://app.pennylane.com/oauth/oauth/token` scopes: `customer_invoices:all`

Update a customer invoice

> ℹ️
> This endpoint requires the following scope: `customer_invoices:all`

## Path parameters

- `id` integer, required

## Headers

- `X-Company-Id` string

## Request body

- union
  - object
    - `date` string, date: Invoice date (ISO 8601)
    - `deadline` string, date: Invoice payment deadline (ISO 8601)
    - `customer_id` integer: Customer identifier
    - `customer_invoice_template_id` integer: The customer invoice template ID
    - `sale_type` union: Type of sale printed on the invoice (Type de vente). `goods` is a supply of goods, `services` is a supply of services, and `goods_and_services` is both. `null` means the type has not been chosen. This is not invoice line `substance`. Omit this field to keep the invoice template default on create, or to leave the invoice unchanged on update. Send `null` to clear it. Finalized invoices cannot be updated with this field.
      - 'goods' | 'services' | 'goods_and_services': Type of sale printed on a customer invoice. This is not invoice line substance.
      - unknown
    - `pdf_invoice_free_text` string, nullable: For example, the contact details of the person to contact
    - `pdf_invoice_subject` string, nullable: Invoice title
    - `pdf_description` string, nullable: Invoice description. Maximum 5,000 characters.
    - `currency` string: Invoice Currency (ISO 4217). Default is EUR.
    - `special_mention` string, nullable: Additional details. maximum 20,000 characters.
    - `discount` object
      - `type` 'absolute' | 'relative', required: Discount type. - absolute if it is an amount - relative if it is a percentage
      - `value` union, required
        - string: Discount value on the total amount before tax of the line
        - unknown
    - `language` 'fr_FR' | 'en_GB' | 'de_DE' | 'es_ES': The local default is based on the thirdparty (customer / supplier ) billing_language . In case the customer is not present, we will default to the company locale.
    - `invoice_lines` object: Add, update, delete invoice lines.
      - `create` union[]
        - union
          - object: Provide only the product_id to automatically populate label, raw_currency_unit_price, unit, vat_rate, and ledger_account_id. You can override any of these fields by providing them explicitly.
            - `product_id` integer, required: The product ID. Auto-fills label, raw_currency_unit_price, unit, vat_rate, and ledger_account_id from the product.
            - `label` string: Line item label
            - `ledger_account_id` integer: The ledger account ID
            - `substance` 'goods' | 'services': Resolves the line's ledger account to the company's goods or services account. When provided together with `product_id`, `product_id` auto-fill runs first. If the resulting ledger account does not match `substance`, the request returns 422. When provided together with `ledger_account_id`, both must refer to the same account. If they do not match, the request returns 422.
            - `raw_currency_unit_price` string: The unit price excluding taxes (undiscounted if a discount is set). Can be set up to 6 decimals.
            - `unit` string: Line item unit (type of unit)
            - `vat_rate` 'FR_1_05' | 'FR_1_75' | 'FR_09' | 'FR_21' | 'FR_40' | 'FR_50' | 'FR_55' | 'FR_60' | 'FR_65' | 'FR_85' | 'FR_92' | 'FR_100' | 'FR_130' | 'FR_15_385' | 'FR_160' | 'FR_196' | 'FR_200' | 'AD_10' | 'AD_45' | 'AD_95' | 'AT_100' | 'AT_130' | 'AT_190' | 'AT_200' | 'BE_60' | 'BE_120' | 'BE_210' | 'BG_90' | 'BG_200' | 'CH_25' | 'CH_37' | 'CH_77' | 'CH_26' | 'CH_38' | 'CH_81' | 'CY_30' | 'CY_50' | 'CY_80' | 'CY_90' | 'CY_190' | 'CZ_100' | 'CZ_120' | 'CZ_150' | 'CZ_210' | 'DE_70' | 'DE_190' | 'DK_250' | 'EE_90' | 'EE_200' | 'EE_220' | 'EE_240' | 'ES_40' | 'ES_70' | 'ES_100' | 'ES_210' | 'FI_100' | 'FI_135' | 'FI_140' | 'FI_240' | 'FI_255' | 'GB_50' | 'GB_200' | 'GR_60' | 'GR_130' | 'GR_170' | 'GR_240' | 'GR_40' | 'HR_50' | 'HR_130' | 'HR_250' | 'HU_50' | 'HU_180' | 'HU_270' | 'IE_48' | 'IE_90' | 'IE_135' | 'IE_210' | 'IE_230' | 'IT_40' | 'IT_50' | 'IT_100' | 'IT_220' | 'LT_50' | 'LT_90' | 'LT_120' | 'LT_210' | 'LU_30' | 'LU_70' | 'LU_80' | 'LU_120' | 'LU_130' | 'LU_140' | 'LU_160' | 'LU_170' | 'LV_50' | 'LV_120' | 'LV_210' | 'MC_09' | 'MC_21' | 'MC_55' | 'MC_85' | 'MC_100' | 'MC_200' | 'MT_50' | 'MT_70' | 'MT_120' | 'MT_180' | 'MU_150' | 'NL_90' | 'NL_210' | 'PL_50' | 'PL_80' | 'PL_230' | 'PT_60' | 'PT_130' | 'PT_160' | 'PT_180' | 'PT_220' | 'PT_230' | 'RO_50' | 'RO_90' | 'RO_110' | 'RO_190' | 'RO_210' | 'SE_60' | 'SE_120' | 'SE_250' | 'SI_50' | 'SI_95' | 'SI_220' | 'SK_50' | 'SK_100' | 'SK_190' | 'SK_200' | 'SK_230' | 'NO_120' | 'NO_150' | 'NO_250' | 'DE_70_BU0002' | 'DE_190_BU0003' | 'DE_70_BU0008' | 'DE_190_BU0009' | 'DE_intracom_70_BU0012' | 'DE_intracom_190_BU0013' | 'DE_190_electronic_BU0226' | 'exempt' | 'DE_intracom_exempt_BU0011' | 'DE_intracom_exempt_BU0047' | 'DE_intracom_exempt_BU0233' | 'DE_exempt_BU0001' | 'DE_exempt_BU0181' | 'DE_exempt_BU0490' | 'intracom_21' | 'intracom_55' | 'intracom_85' | 'intracom_100' | 'crossborder' | 'ES_intracom_sales_goods' | 'ES_intracom_sales_services' | 'DE_intracom_70_BU0018' | 'DE_intracom_190_BU0019' | 'DE_intracom_190_BU0506' | 'extracom' | 'DE_extracom_BU0173' | 'DE_extracom_BU0191' | 'DE_extracom_190_BU0511' | 'ES_isp_sales' | 'ES_suplido' | 'FR_85_construction' | 'FR_100_construction' | 'FR_200_construction' | 'DE_exempt_construction_BU0046' | 'DE_70_construction_BU0091' | 'DE_190_construction_BU0094' | 'DE_190_construction_BU0526' | 'DE_190_construction_BU0546' | 'ES_isp_purchases_210' | 'ES_isp_purchases_100' | 'mixed': Product VAT rate. A 20% VAT in France is FR_200.
            - `quantity` number, required: Line item quantity (number of items)
            - `section_rank` integer: Has to correspond to the rank number of a line items section in which the line item should be
            - `discount` object
              - …
            - `description` string, nullable: The description of the invoice line
            - `custom_fields` object[]: **Experimental** — under active development, may change without notice. Do not rely on this field in production integrations.
              - …
            - `performance_period` union
              - …
          - object: Create an invoice line by providing all required fields explicitly without referencing a product.
            - `label` string, required: Line item label
            - `ledger_account_id` integer: The ledger account ID
            - `substance` 'goods' | 'services': Resolves the line's ledger account to the company's goods or services account. When provided together with `ledger_account_id`, both must refer to the same account. If they do not match, the request returns 422.
            - `raw_currency_unit_price` string, required: The unit price excluding taxes (undiscounted if a discount is set). Can be set up to 6 decimals.
            - `unit` string, required: Line item unit (type of unit)
            - `vat_rate` 'FR_1_05' | 'FR_1_75' | 'FR_09' | 'FR_21' | 'FR_40' | 'FR_50' | 'FR_55' | 'FR_60' | 'FR_65' | 'FR_85' | 'FR_92' | 'FR_100' | 'FR_130' | 'FR_15_385' | 'FR_160' | 'FR_196' | 'FR_200' | 'AD_10' | 'AD_45' | 'AD_95' | 'AT_100' | 'AT_130' | 'AT_190' | 'AT_200' | 'BE_60' | 'BE_120' | 'BE_210' | 'BG_90' | 'BG_200' | 'CH_25' | 'CH_37' | 'CH_77' | 'CH_26' | 'CH_38' | 'CH_81' | 'CY_30' | 'CY_50' | 'CY_80' | 'CY_90' | 'CY_190' | 'CZ_100' | 'CZ_120' | 'CZ_150' | 'CZ_210' | 'DE_70' | 'DE_190' | 'DK_250' | 'EE_90' | 'EE_200' | 'EE_220' | 'EE_240' | 'ES_40' | 'ES_70' | 'ES_100' | 'ES_210' | 'FI_100' | 'FI_135' | 'FI_140' | 'FI_240' | 'FI_255' | 'GB_50' | 'GB_200' | 'GR_60' | 'GR_130' | 'GR_170' | 'GR_240' | 'GR_40' | 'HR_50' | 'HR_130' | 'HR_250' | 'HU_50' | 'HU_180' | 'HU_270' | 'IE_48' | 'IE_90' | 'IE_135' | 'IE_210' | 'IE_230' | 'IT_40' | 'IT_50' | 'IT_100' | 'IT_220' | 'LT_50' | 'LT_90' | 'LT_120' | 'LT_210' | 'LU_30' | 'LU_70' | 'LU_80' | 'LU_120' | 'LU_130' | 'LU_140' | 'LU_160' | 'LU_170' | 'LV_50' | 'LV_120' | 'LV_210' | 'MC_09' | 'MC_21' | 'MC_55' | 'MC_85' | 'MC_100' | 'MC_200' | 'MT_50' | 'MT_70' | 'MT_120' | 'MT_180' | 'MU_150' | 'NL_90' | 'NL_210' | 'PL_50' | 'PL_80' | 'PL_230' | 'PT_60' | 'PT_130' | 'PT_160' | 'PT_180' | 'PT_220' | 'PT_230' | 'RO_50' | 'RO_90' | 'RO_110' | 'RO_190' | 'RO_210' | 'SE_60' | 'SE_120' | 'SE_250' | 'SI_50' | 'SI_95' | 'SI_220' | 'SK_50' | 'SK_100' | 'SK_190' | 'SK_200' | 'SK_230' | 'NO_120' | 'NO_150' | 'NO_250' | 'DE_70_BU0002' | 'DE_190_BU0003' | 'DE_70_BU0008' | 'DE_190_BU0009' | 'DE_intracom_70_BU0012' | 'DE_intracom_190_BU0013' | 'DE_190_electronic_BU0226' | 'exempt' | 'DE_intracom_exempt_BU0011' | 'DE_intracom_exempt_BU0047' | 'DE_intracom_exempt_BU0233' | 'DE_exempt_BU0001' | 'DE_exempt_BU0181' | 'DE_exempt_BU0490' | 'intracom_21' | 'intracom_55' | 'intracom_85' | 'intracom_100' | 'crossborder' | 'ES_intracom_sales_goods' | 'ES_intracom_sales_services' | 'DE_intracom_70_BU0018' | 'DE_intracom_190_BU0019' | 'DE_intracom_190_BU0506' | 'extracom' | 'DE_extracom_BU0173' | 'DE_extracom_BU0191' | 'DE_extracom_190_BU0511' | 'ES_isp_sales' | 'ES_suplido' | 'FR_85_construction' | 'FR_100_construction' | 'FR_200_construction' | 'DE_exempt_construction_BU0046' | 'DE_70_construction_BU0091' | 'DE_190_construction_BU0094' | 'DE_190_construction_BU0526' | 'DE_190_construction_BU0546' | 'ES_isp_purchases_210' | 'ES_isp_purchases_100' | 'mixed', required: Product VAT rate. A 20% VAT in France is FR_200.
            - `quantity` number, required: Line item quantity (number of items)
            - `section_rank` integer: Has to correspond to the rank number of a line items section in which the line item should be
            - `discount` object
              - …
            - `description` string, nullable: The description of the invoice line
            - `custom_fields` object[]: **Experimental** — under active development, may change without notice. Do not rely on this field in production integrations.
              - …
            - `performance_period` union
              - …
      - `update` object[]
        - `id` integer, required: ID of the invoice line
        - `label` string: Line item label
        - `quantity` number: Line item quantity (number of items)
        - `ledger_account_id` integer: The ledger account ID
        - `raw_currency_unit_price` string: The unit price excluding taxes. Can be set up to 6 decimals.
        - `unit` string: Line item unit (type of unit)
        - `vat_rate` 'FR_1_05' | 'FR_1_75' | 'FR_09' | 'FR_21' | 'FR_40' | 'FR_50' | 'FR_55' | 'FR_60' | 'FR_65' | 'FR_85' | 'FR_92' | 'FR_100' | 'FR_130' | 'FR_15_385' | 'FR_160' | 'FR_196' | 'FR_200' | 'AD_10' | 'AD_45' | 'AD_95' | 'AT_100' | 'AT_130' | 'AT_190' | 'AT_200' | 'BE_60' | 'BE_120' | 'BE_210' | 'BG_90' | 'BG_200' | 'CH_25' | 'CH_37' | 'CH_77' | 'CH_26' | 'CH_38' | 'CH_81' | 'CY_30' | 'CY_50' | 'CY_80' | 'CY_90' | 'CY_190' | 'CZ_100' | 'CZ_120' | 'CZ_150' | 'CZ_210' | 'DE_70' | 'DE_190' | 'DK_250' | 'EE_90' | 'EE_200' | 'EE_220' | 'EE_240' | 'ES_40' | 'ES_70' | 'ES_100' | 'ES_210' | 'FI_100' | 'FI_135' | 'FI_140' | 'FI_240' | 'FI_255' | 'GB_50' | 'GB_200' | 'GR_60' | 'GR_130' | 'GR_170' | 'GR_240' | 'GR_40' | 'HR_50' | 'HR_130' | 'HR_250' | 'HU_50' | 'HU_180' | 'HU_270' | 'IE_48' | 'IE_90' | 'IE_135' | 'IE_210' | 'IE_230' | 'IT_40' | 'IT_50' | 'IT_100' | 'IT_220' | 'LT_50' | 'LT_90' | 'LT_120' | 'LT_210' | 'LU_30' | 'LU_70' | 'LU_80' | 'LU_120' | 'LU_130' | 'LU_140' | 'LU_160' | 'LU_170' | 'LV_50' | 'LV_120' | 'LV_210' | 'MC_09' | 'MC_21' | 'MC_55' | 'MC_85' | 'MC_100' | 'MC_200' | 'MT_50' | 'MT_70' | 'MT_120' | 'MT_180' | 'MU_150' | 'NL_90' | 'NL_210' | 'PL_50' | 'PL_80' | 'PL_230' | 'PT_60' | 'PT_130' | 'PT_160' | 'PT_180' | 'PT_220' | 'PT_230' | 'RO_50' | 'RO_90' | 'RO_110' | 'RO_190' | 'RO_210' | 'SE_60' | 'SE_120' | 'SE_250' | 'SI_50' | 'SI_95' | 'SI_220' | 'SK_50' | 'SK_100' | 'SK_190' | 'SK_200' | 'SK_230' | 'NO_120' | 'NO_150' | 'NO_250' | 'DE_70_BU0002' | 'DE_190_BU0003' | 'DE_70_BU0008' | 'DE_190_BU0009' | 'DE_intracom_70_BU0012' | 'DE_intracom_190_BU0013' | 'DE_190_electronic_BU0226' | 'exempt' | 'DE_intracom_exempt_BU0011' | 'DE_intracom_exempt_BU0047' | 'DE_intracom_exempt_BU0233' | 'DE_exempt_BU0001' | 'DE_exempt_BU0181' | 'DE_exempt_BU0490' | 'intracom_21' | 'intracom_55' | 'intracom_85' | 'intracom_100' | 'crossborder' | 'ES_intracom_sales_goods' | 'ES_intracom_sales_services' | 'DE_intracom_70_BU0018' | 'DE_intracom_190_BU0019' | 'DE_intracom_190_BU0506' | 'extracom' | 'DE_extracom_BU0173' | 'DE_extracom_BU0191' | 'DE_extracom_190_BU0511' | 'ES_isp_sales' | 'ES_suplido' | 'FR_85_construction' | 'FR_100_construction' | 'FR_200_construction' | 'DE_exempt_construction_BU0046' | 'DE_70_construction_BU0091' | 'DE_190_construction_BU0094' | 'DE_190_construction_BU0526' | 'DE_190_construction_BU0546' | 'ES_isp_purchases_210' | 'ES_isp_purchases_100' | 'mixed': Product VAT rate. A 20% VAT in France is FR_200.
        - `description` string, nullable: The description of the invoice line
        - `product_id` integer: The product ID
        - `discount` object
          - `type` 'absolute' | 'relative', required: Discount type. - absolute if it is an amount - relative if it is a percentage
          - `value` string, required: Discount value on the total amount before tax of the line
        - `section_rank` integer: Has to correspond to the rank number of an invoice line section in which the invoice line should be
        - `custom_fields` object[]: **Experimental** — under active development, may change without notice. Do not rely on this field in production integrations.
          - `name` string, required
          - `value` union, required: Format depends on the field type: plain string for `text`; numeric string for `number` (e.g. `"42.5"`); ISO 8601 date for `date` (e.g. `"2025-01-15"`); option label for `single_select`; array of option labels for `multi_select`.
            - string, nullable
            - string[]
        - `performance_period` union
          - object: Date range during which the service or delivery was performed (ISO 8601). Used for German VAT accounting (Leistungsdatum). Only accepted for companies using German accounting, otherwise the request is rejected. Omit it to keep the current performance period, or set it to null to remove it.
            - `start_date` string, date, nullable: Start date of the performance period (ISO 8601). Null for single-date types (`delivery_date`, `service_date`), required otherwise.
            - `end_date` string, date, required: End date of the performance period (ISO 8601)
            - `date_type` 'delivery_date' | 'service_date' | 'delivery_period' | 'service_period', required: Whether the performance period represents a single date or a range, and whether it is a delivery or service. - delivery_date: single delivery date (end_date only) - service_date: single service date (end_date only) - delivery_period: delivery date range (start_date and end_date) - service_period: service date range (start_date and end_date)
          - unknown
      - `delete` object[]: Delete invoice lines. Any cutoffs (deferrals, assets, or advances) associated with the deleted invoice lines will be automatically destroyed unless they have generated ledger events, in which case deletion will fail.
        - `id` integer, required: ID of the invoice line
    - `label` string, nullable: Custom label for the invoice used on accounting (ledger) entries. If not provided, Pennylane generates a label automatically.
    - `external_reference` string: The unique external reference that was assigned during creation either by you or Pennylane.
    - `installments` object: Add, update, delete installments. The final installments amounts need to be balanced so that their total is equal to the customer invoice total with tax. Installments with `paid`, `cancelled` or `waiting_funds` status cannot be updated or deleted. Not allowed to go from a multi-installment invoice to a single installment invoice (e.g. from 3 installments to only 1 installment). > ℹ️ > This param requires a company plan that has access to Installments, otherwise responds with 403. > ⚠️ **Warning**: This feature is in alpha > The installments param will be ignored until further rollout and the request will proceed as if it was made without it > If you plan to rely on it, please get in touch with us first
      - `create` object[]
        - `deadline` string, date, required: Installment payment deadline (ISO 8601)
        - `amount` string, required: Installment amount (in invoice currency)
        - `payment_methods` string[]
      - `update` object[]
        - `id` integer, required: ID of the installment
        - `deadline` string, date: Installment payment deadline (ISO 8601)
        - `amount` string: Installment amount (in invoice currency)
        - `payment_methods` string[]
      - `delete` object[]: Delete installments. If you have only 1 installment you can't delete it, unless you create other(s) in the same request.
        - `id` integer, required: ID of the installment
  - object
    - `label` string, nullable: Custom label for the invoice used on accounting (ledger) entries. If not provided, Pennylane generates a label automatically.
    - `transaction_reference` object, nullable: Schema for creating/updating a transaction reference
      - `banking_provider` 'stripe' | 'gocardless' | 'bank' | 'budgetinsight' | 'helloasso', required: The banking provider for the transaction
      - `provider_field_name` 'payment_id' | 'charge_id' | 'report_id' | 'webid' | 'label' | 'order_id', required: Name of the field that you want to match
      - `provider_field_value` string, required: Value that you want to match. With `banking_provider: stripe` or `gocardless`, the value is matched exactly against the reference with the same `provider_field_name` synced from that provider, on non-archived transactions. With `banking_provider: bank` and `provider_field_name: label`, every non-archived bank transaction whose label contains this value as a whole word is matched, including transactions already matched to other documents. The match is case-sensitive, and a value that does not start and end with a letter, digit or underscore never matches. Use a value that identifies a single payment, such as a card transaction ID: a merchant name or a short generic value groups unrelated documents together. Pass the literal string `invoice_number` to match on this invoice's number instead of a fixed value. Other combinations do not match any transaction. In all cases, matching runs again on every update of this invoice.
    - `invoice_lines` object: Update invoice lines.
      - `update` object[]
        - `id` integer, required: Invoice line identifier
        - `imputation_dates` object
          - `start_date` string, date, required: Start date of the imputation period (ISO 8601)
          - `end_date` string, date, required: End date of the imputation period (ISO 8601)
        - `performance_period` union
          - object: Date range during which the service or delivery was performed (ISO 8601). Used for German VAT accounting (Leistungsdatum). Only accepted for companies using German accounting, otherwise the request is rejected. Omit it to keep the current performance period, or set it to null to remove it.
            - `start_date` string, date, nullable: Start date of the performance period (ISO 8601). Null for single-date types (`delivery_date`, `service_date`), required otherwise.
            - `end_date` string, date, required: End date of the performance period (ISO 8601)
            - `date_type` 'delivery_date' | 'service_date' | 'delivery_period' | 'service_period', required: Whether the performance period represents a single date or a range, and whether it is a delivery or service. - delivery_date: single delivery date (end_date only) - service_date: single service date (end_date only) - delivery_period: delivery date range (start_date and end_date) - service_period: service date range (start_date and end_date)
          - unknown
    - `external_reference` string: The unique external reference that was assigned during creation either by you or Pennylane.
    - `installments` object: Add, update, delete installments. The final installments amounts need to be balanced so that their total is equal to the customer invoice total with tax. Installments with `paid`, `cancelled` or `waiting_funds` status cannot be updated or deleted. Not allowed to go from a multi-installment invoice to a single installment invoice (e.g. from 3 installments to only 1 installment). > ℹ️ > This param requires a company plan that has access to Installments, otherwise responds with 403. > ⚠️ **Warning**: This feature is in alpha > The installments param will be ignored until further rollout and the request will proceed as if it was made without it > If you plan to rely on it, please get in touch with us first
      - `create` object[]
        - `deadline` string, date, required: Installment payment deadline (ISO 8601)
        - `amount` string, required: Installment amount (in invoice currency)
        - `payment_methods` string[]
      - `update` object[]
        - `id` integer, required: ID of the installment
        - `deadline` string, date: Installment payment deadline (ISO 8601)
        - `amount` string: Installment amount (in invoice currency)
        - `payment_methods` string[]
      - `delete` object[]: Delete installments. If you have only 1 installment you can't delete it, unless you create other(s) in the same request.
        - `id` integer, required: ID of the installment

## Response `200`

A Customer Invoice

- object
  - `id` integer, required: Invoice identifier
  - `label` string, nullable, required
  - `invoice_number` string, required
  - `currency` 'EUR' | 'USD' | 'GBP' | 'AED' | 'AFN' | 'ALL' | 'AMD' | 'ANG' | 'AOA' | 'ARS' | 'AUD' | 'AWG' | 'AZN' | 'BAM' | 'BBD' | 'BDT' | 'BGN' | 'BHD' | 'BIF' | 'BMD' | 'BND' | 'BOB' | 'BRL' | 'BSD' | 'BTN' | 'BWP' | 'BYN' | 'BYR' | 'BZD' | 'CAD' | 'CDF' | 'CHE' | 'CHF' | 'CLF' | 'CLP' | 'CNY' | 'COP' | 'CRC' | 'CUC' | 'CUP' | 'CVE' | 'CZK' | 'DJF' | 'DKK' | 'DOP' | 'DZD' | 'EGP' | 'ERN' | 'ETB' | 'FJD' | 'FKP' | 'GEL' | 'GGP' | 'GHS' | 'GIP' | 'GMD' | 'GNF' | 'GTQ' | 'GYD' | 'HKD' | 'HNL' | 'HRK' | 'HTG' | 'HUF' | 'IDR' | 'ILS' | 'IMP' | 'INR' | 'IQD' | 'IRR' | 'ISK' | 'JEP' | 'JMD' | 'JOD' | 'JPY' | 'KES' | 'KGS' | 'KHR' | 'KMF' | 'KPW' | 'KRW' | 'KWD' | 'KYD' | 'KZT' | 'LAK' | 'LBP' | 'LKR' | 'LRD' | 'LSL' | 'LTL' | 'LVL' | 'LYD' | 'MAD' | 'MDL' | 'MGA' | 'MKD' | 'MMK' | 'MNT' | 'MOP' | 'MRO' | 'MRU' | 'MUR' | 'MVR' | 'MWK' | 'MXN' | 'MYR' | 'MZN' | 'NAD' | 'NGN' | 'NIO' | 'NOK' | 'NPR' | 'NZD' | 'OMR' | 'PAB' | 'PEN' | 'PGK' | 'PHP' | 'PKR' | 'PLN' | 'PYG' | 'QAR' | 'RON' | 'RSD' | 'RUB' | 'RWF' | 'SAR' | 'SBD' | 'SCR' | 'SDG' | 'SEK' | 'SGD' | 'SHP' | 'SLL' | 'SLE' | 'SOS' | 'SRD' | 'STD' | 'SVC' | 'SYP' | 'SZL' | 'THB' | 'TJS' | 'TMT' | 'TND' | 'TOP' | 'TRY' | 'TTD' | 'TWD' | 'TZS' | 'UAH' | 'UGX' | 'UYU' | 'UZS' | 'VEF' | 'VES' | 'VND' | 'VUV' | 'WST' | 'XAF' | 'XCD' | 'XDR' | 'XOF' | 'XPF' | 'YER' | 'ZAR' | 'ZMK' | 'ZMW' | 'ZWL', required
  - `amount` string, required: Invoice amount (total value of the invoice in euros. If the currency is euro, `currency_amount` and `amount` are identical)
  - `currency_amount` string, required: Invoice currency amount (total value of the invoice in the currency of the invoice)
  - `currency_amount_before_tax` string, required: Invoice currency amount before tax (total value before tax of the invoice in the currency of the invoice)
  - `exchange_rate` string, required: Invoice exchange rate (used to convert the invoice to euros. If the invoice currency is euro it will be 1.0)
  - `date` string, date, nullable, required: Invoice issue date (ISO 8601)
  - `deadline` string, date, nullable, required: Invoice payment deadline (ISO 8601)
  - `performance_date` string, date, nullable, required: End date of the performance period of the first invoice line that has one (ISO 8601). Used for German VAT accounting (Leistungsdatum). Null if the company does not use German accounting, or if no invoice line has a performance period.
  - `currency_tax` string, required: Invoice taxable amount (in invoice currency)
  - `tax` string, required: Invoice taxable amount (in euros). If the currency is euro, `currency_tax` and `tax` are identical.
  - `language` 'fr_FR' | 'en_GB' | 'de_DE' | 'es_ES', required: The local default is based on the thirdparty (customer / supplier ) billing_language . In case the customer is not present, we will default to the company locale.
  - `paid` boolean, required: Invoice paid status (set to True if the invoice is paid)
  - `status` 'archived' | 'incomplete' | 'cancelled' | 'paid' | 'partially_paid' | 'partially_cancelled' | 'upcoming' | 'late' | 'draft' | 'credit_note' | 'proforma' | 'shipping_order' | 'purchasing_order' | 'estimate_pending' | 'estimate_accepted' | 'estimate_invoiced' | 'estimate_denied', required
  - `discount` object, required
    - `type` 'absolute' | 'relative', required: Discount type. - absolute if it is an amount - relative if it is a percentage
    - `value` string, required: Discount value on the total amount before tax of the line
  - `ledger_entry` object, required
    - `id` integer, required: Ledger entry identifier
  - `public_file_url` string, nullable, required: Public URL of the invoice file. The URL will expire after 30 minutes. May be null if the PDF has not been generated yet (for example right after creation or finalization); fetch the invoice again to get the URL once it is available.
  - `filename` string, nullable, required: Name of the file attached to the invoice. May be null immediately after creation if the PDF has not been generated yet; fetch the invoice again to get the filename once it is available.
  - `remaining_amount_with_tax` string, nullable, required: The remaining amount with VAT to pay for the invoice to be considered paid
  - `remaining_amount_without_tax` string, nullable, required: The remaining amount without VAT to pay for the invoice to be considered paid
  - `draft` boolean, required: Indicates if the invoice is in draft (has not been finalized)
  - `special_mention` string, nullable, required: Additional details
  - `sale_type` union, required: Type of sale printed on the invoice (Type de vente). `goods` is a supply of goods, `services` is a supply of services, and `goods_and_services` is both. `null` means the type has not been chosen. This is not invoice line `substance`.
    - 'goods' | 'services' | 'goods_and_services': Type of sale printed on a customer invoice. This is not invoice line substance.
    - unknown
  - `customer` object, nullable, required
    - `id` integer, required
    - `url` string, required: URL to get the customer.
  - `payment_method_ledger_account` object, nullable, required: Ledger account used for reconciliation by payment method. When present, the receivable was transferred to this account.
    - `id` integer, required
    - `url` string, required: URL to get the ledger account.
  - `invoice_line_sections` object, required
    - `url` string, required: URL to get the invoice line sections of the invoice.
  - `invoice_lines` object, required
    - `url` string, required: URL to get the invoice lines of the invoice.
  - `custom_header_fields` object, required
    - `url` string, required: URL to get the custom header fields of the invoice.
  - `categories` object, required
    - `url` string, required: URL to get the categories of the invoice.
  - `pdf_invoice_free_text` string, required
  - `pdf_invoice_subject` string, required
  - `pdf_description` string, nullable, required
  - `billing_subscription` object, nullable, required
    - `id` integer, required
  - `credited_invoice` object, nullable, required: The credited invoice if the invoice is a credit note.
    - `id` integer, required
    - `url` string, required: URL to get the credited invoice.
  - `customer_invoice_template` object, nullable, required
    - `id` integer, required
  - `transaction_reference` object, nullable, required: This reconciles the invoice with a transaction. See documentation about [automatic payment matching](https://pennylane.readme.io/v2.0/docs/automating-payment-matching).
    - `banking_provider` string, required: The banking provider for the transaction
    - `provider_field_name` string, required: Name of the field that you want to match
    - `provider_field_value` string, required: Value that you want to match
  - `payments` object, required
    - `url` string, required: URL to get the payments of the invoice.
  - `matched_transactions` object, required
    - `url` string, required: URL to get the transactions of the invoice.
  - `appendices` object, required
    - `url` string, required: URL to get the appendices of the invoice.
  - `installments` object, nullable, required: > ⚠️ **Warning**: This feature is in alpha > Until further rollout, this field will be `null` for all invoices. > If you plan to rely on it, please get in touch with us first.
    - `url` string, required: URL to get the installments of the invoice.
  - `quote` object, nullable, required: The quote at the origin of the invoice
    - `id` integer, required
  - `external_reference` string, required: The unique external reference that was assigned during creation either by you or Pennylane. (Same attribute as `external_id` in the API v1)
  - `purchase_order_reference` string, nullable, required: Buyer's purchase order reference (e-invoice field BT-13). Maximum 50 characters.
  - `contract_reference` string, nullable, required: Contract reference (e-invoice field BT-12). Maximum 50 characters.
  - `sales_order_reference` string, nullable, required: Seller's sales order reference (e-invoice field BT-14). Maximum 50 characters.
  - `e_invoicing` object, nullable, required: E-invoicing lifecycle information managed by the PA. Contains the current status and associated reason when applicable. This field is only present for invoices sent through e-invoicing.
    - `status` 'accepted' | 'approved' | 'collected' | 'in_dispute' | 'partially_collected' | 'refused' | 'rejected' | 'sent' | 'submitted', required
    - `reason` string, nullable, required
    - `flow` object, nullable, required: The AFNOR flow associated with this e-invoice.
      - `id` string, uuid, required
    - `lifecycle_events` object[], nullable, required: > ⚠️ **Warning**: This feature is in beta. > Until further rollout, this field will be `null` for all invoices. > If you plan to rely on it, please get in touch with us first. Chronologically ordered e-invoicing lifecycle events received from the PA. Some events are optional and only present when the receiving platform reports them.
      - `type` 'accepted' | 'available_to_buyer' | 'emitted_by_seller_platform' | 'in_dispute' | 'partially_collected' | 'received_by_buyer_platform' | 'refused' | 'rejected' | 'sent' | 'submitted', required
      - `timestamp` string, date-time, required
  - `factur_x` boolean, required: Whether the attached invoice file is a Factur-X document (a PDF/A-3 with an embedded structured XML).
  - `schematron_validation_status` 'pending' | 'valid' | 'invalid', nullable, required: Result of Schematron structural validation for Factur-X invoices. Only relevant when `factur_x` is true, null otherwise.
  - `archived_at` string, date-time, nullable, required: The time the invoice has been archived
  - `created_at` string, date-time, required: The time the invoice has been created
  - `updated_at` string, date-time, required: The last time the invoice has been updated

## Other responses

- `400`: Bad request
- `401`: Access token is missing or invalid
- `403`: Access to this resource forbidden
- `404`: The resource was not found
- `422`: Unprocessable content

## Changes

- **2026-11-03** `871f863307e5`: 2 warning, 45 info
  - added the new `VES` enum value to the `currency` response property for the response status `200`
  - added the new `es_ES` enum value to the `language` response property for the response status `200`
  - added the new optional `header` request parameter `X-Company-Id`
  - added the new optional request property `anyOf[subschema #1: Draft Customer Invoice]/installments`
  - …43 more

[Operation history](https://skmtc.dev/pennylane/apis/company-v2/changes/api/external/v2/customer_invoices/:id/put.md)

---

[API](https://skmtc.dev/pennylane/apis/company-v2.md) - [All operations](https://skmtc.dev/pennylane/apis/company-v2/llms.txt) - [OpenAPI document](https://skmtc.dev/pennylane/apis/company-v2/revisions/871f863307e5?raw)
