---
title: "Turn the draft invoice into a finalized invoice."
method: PUT
path: "/api/external/v2/customer_invoices/{id}/finalize"
tags: ["Customer Invoices"]
---

# Turn the draft invoice into a finalized invoice.

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

## Request

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

Convert the draft customer invoice or credit note into a finalized
one. Once finalized, the resource can no longer be edited.

Finalizing requires an `invoice` numbering.
`GET /api/external/v2/numberings` lists whether it is configured.


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

## Path parameters

- `id` integer, required

## Headers

- `X-Company-Id` string

## 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, 9 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 required property `contract_reference` to the response with the `200` status
  - …7 more

[Operation history](https://skmtc.dev/pennylane/apis/company-v2/changes/api/external/v2/customer_invoices/:id/finalize/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)
