---
title: "Create an invoice"
method: POST
path: "/companies/{company}/invoices"
tags: ["Invoices"]
---

# Create an invoice

`POST /companies/{company}/invoices`

## Request

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

Create an invoice in `draft`, or issue it at once with `options.issue_directly: true`.
- Send an `Idempotency-Key`; a duplicate `external_id` upserts. A `201` is not AEAT acceptance.
- `type: F2`, `payment` or `options.register_verifactu` make it a one-call checkout (paid simplified invoice).

**Signing certificate (NO VERI*FACTU).** Only in that mode, a missing or invalid company certificate is rejected before the invoice is issued: 422 `verifactu_not_eligible`, `error.subcode: signing_certificate_unavailable`; nothing is consumed, so fix it and repeat the call.

## Path parameters

- `company` string, uuid, required

## Headers

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

## Request body

- CreateInvoiceRequest: Create an invoice. Required: `series_id`, `issued_on`, `due_on`, `lines[]` (at least one) and `client_id` — except on a simplified invoice (`type: F2`), where the client is optional. A terminal can issue an already-paid simplified invoice in ONE idempotent call (unattended checkout): send `type: F2`, `prices_include_tax`, a `payment` block and `options.register_verifactu`, and identify the operation with `external_id`. Optional: `notes`, `metadata`, `tags`, `custom_fields`, and an `options` object to atomically create, issue, send and wait for the PDF in a single call. Without `options` the invoice is created as a draft. A line may also be a DISBURSEMENT (`line_type: "SUPLIDO"`): an amount paid in the name and on behalf of the client (an official fee, duty or registry charge) that is re-invoiced at cost and, under art. 78.Tres.3 of the Spanish VAT Act (LIVA), stays out of the taxable base — it carries no VAT, withholding, surcharge, discount or product, requires `source_invoice_reference`, and is not allowed on a simplified (`F2`) invoice. Worked example: a 1,000.00 service line at 21% plus a 150.00 `SUPLIDO` line returns `subtotal` 1000.00, `taxes_total` 210.00, `total` 1210.00, `total_disbursements` 150.00 and `total_to_pay` 1360.00.
  - `type` 'F1' | 'F2' | 'null', nullable: Invoice type: `F1` (complete invoice, the default) or `F2` (simplified invoice, the ticket of a terminal). A simplified invoice needs simplified invoices enabled for the company (422 `simplified_invoices_disabled`) and cannot exceed the absolute cap of 3,000 € VAT included (422 `simplified_invoice_not_allowed`).
  - `client_id` string, uuid: Id of the client (UUID v7). Required with `type: F1` (or without `type`), and the client must have a complete tax ID. Optional with `type: F2`: without it the invoice is an anonymous ticket; with it, a qualified simplified invoice (the client must belong to your company).
  - `prices_include_tax` boolean, nullable: When `true`, the `unit_price` of every line is the FINAL price with taxes (VAT included) and the system computes the net base to the cent, so the invoice total equals the sum of the amounts you sent. If no distribution of the cents reaches that total the request is rejected with 422 `amount_reconciliation_failed` and nothing is issued. Catalog lines (`product_id`) are not accepted in this mode. Defaults to `false`.
  - `operation_on` string, date, nullable: Date the operation took place (YYYY-MM-DD) when it differs from the issue date (arts. 6.1.f and 7.1.c of Royal Decree 1619/2012). It cannot be later than `issued_on` (422 `operation_date_after_issue_date`) unless the first line that declares a `regime_key` uses 14 or 15. It is printed on the PDF and reported in the VeriFactu record.
  - `payment` object: Payment recorded in the same call: after issuing, a payment is registered for the whole amount due and the invoice ends up paid. It implies issuing. Its presence makes the request an unattended checkout.
    - `method` 'bank_transfer' | 'direct_debit' | 'sepa_direct_debit' | 'cash' | 'credit_card' | 'check' | 'paypal' | 'bizum' | 'other', required: Payment method, from the catalog of `GET /v1/payment-methods`. Required when `payment` is sent.
    - `paid_at` string, date-time, nullable: Date of the payment (ISO 8601); only its date is used. Defaults to the issue date.
    - `reference` string, nullable: Reference of the payment, for example the transaction id of the card terminal (up to 100 characters).
  - `series_id` string, uuid, nullable
  - `price_list_id` string, uuid, nullable
  - `reprice_strategy` 'existing_catalog_lines' | 'future_lines_only' | 'null', nullable
  - `issued_on` string, date, required
  - `due_on` string, date, required
  - `notes` string, nullable
  - `external_id` 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
  - `lines` object[], required
    - `description` string, nullable
    - `additional_description` string, nullable
    - `quantity` number, required
    - `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
    - `tax_rate_id` string, uuid, nullable
    - `tax_rate` number, nullable: VAT rate of the line (0–100). Optional: when you omit it (or send `null`) the line takes, in this order, the tax it references or the tax assigned to its product, and otherwise the DEFAULT TAX of your company for this kind of document (tax settings), 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. The line ends up exactly as if you had chosen that tax. 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): the API never guesses a rate. A `0` you send is honoured as a real 0 % rate, never confused with «not stated».
    - `retention_rate` number, nullable
    - `surcharge_rate` number, nullable
    - `retention_rate_id` string, uuid, nullable
    - `surcharge_rate_id` string, uuid, nullable
    - `product_id` string, uuid, nullable
    - `variant_id` string, uuid, nullable
    - `presentation_id` string, uuid, nullable
    - `confirmed_base_quantity` number, nullable
    - `discount_percent` number, nullable
    - `regime_key` '01' | '02' | '03' | '04' | '05' | '06' | '07' | '08' | '09' | '10' | '11' | '14' | '15' | '17' | '18' | '19' | '20' | 'null', nullable
    - `exemption_reason` 'E1' | 'E2' | 'E3' | 'E4' | 'E5' | 'E6' | 'N1' | 'N2' | 'null', nullable
    - `indirect_tax_regime` 'iva' | 'igic' | 'ipsi' | 'null', nullable
    - `line_type` 'NORMAL' | 'SUPLIDO' | 'null', nullable: Kind of line: `NORMAL` (default) for an ordinary line of your own operation, or `SUPLIDO` for a DISBURSEMENT — an amount you paid in the name and on behalf of the client (an official fee, duty or registry charge) and now re-invoice at cost. A disbursement is not part of your taxable base (art. 78.Tres.3 LIVA): it stays out of `subtotal`/`taxes_total`/`total`, is aggregated into `total_disbursements`, and is never declared in the AEAT VeriFactu record. A `SUPLIDO` line must carry no VAT, withholding, surcharge, discount, regime key, exemption cause or product, and is rejected on a simplified (`F2`) invoice.
    - `source_invoice_reference` string, nullable: Reference of the supporting document that originated the disbursement — the receipt or fee number issued by the public body (up to 100 characters). REQUIRED when `line_type` is `SUPLIDO`; leave it out on a normal line. Free text on purpose: the receipt of a public body is rarely a supplier invoice.
    - `source_invoice_ids` string[], nullable: Optional traceability of a disbursement: list of IDs (UUID v7) of your own expenses that back it. An expense of another company is rejected with 422. Omit it (or send `null`) when there is nothing to link.
    - `unit` string, nullable: Unit of measure printed next to the quantity on the document (`hours`, `kg`, `units`, …), up to 20 characters. Presentation only: free text, no closed catalog and no fiscal effect.
    - `exemption_reason_text` string, nullable: Free-text wording of the exemption provision of this line (up to 255 characters), printed under the line description to satisfy the mention required by art. 6.1.j of Royal Decree 1619/2012 when the catalogued cause does not cover it. Orthogonal to `exemption_reason` and to the document-level exemption cause; no coherence is enforced between them.
    - `line_total` number, nullable: Optional CHECKSUM of the line total. When sent, it is compared against the total this API computes and the request is rejected with 422 (`line_total_checksum_mismatch`, with the expected and received values in `error.details`) when they differ by more than one cent. Never stored and never returned: the invoiced amount is always the computed one, so this field only reports a rounding mismatch with your own ERP. Omit it and no checksum runs.
    - `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
  - `options` object
    - `issue_directly` boolean, nullable: When `true`, the invoice is issued right after it is created (definitive number, frozen document) instead of staying as a draft.
    - `send_automatically` boolean, nullable: When `true`, the invoice is emailed once issued; it implies issuing. The recipient is `options.send_to` or, when you omit it, the email of the client. If there is no possible recipient (no client, or a client with no email, and no `send_to`) the request is rejected with 422 `missing_required_param` and `param` = `options.send_to` BEFORE anything is created: no draft is left behind and no number is consumed. Repeating the request of an `external_id` that is already issued does not email it again while that email is queued or delivered.
    - `send_to` string, email, nullable: Recipient email for `options.send_automatically`. Optional when the client has an email; required (422 `missing_required_param`) when you ask for the sending with no client or with a client that has no email. An empty value is rejected as well.
    - `wait_for_pdf` boolean, nullable: When `true`, the response waits up to about 15 seconds for the A4 PDF to be materialized (`pdf.status: ready`); if it is not ready by then `pdf.status` is `pending`. It implies issuing.
    - `register_verifactu` boolean, nullable: When `true`, the VeriFactu alta is generated synchronously, BEFORE responding, and the response carries its huella and QR in the `verifactu` block. It implies issuing. If the alta cannot be generated the invoice stays issued and the block reports `status: failed` with its `error_code`. Its transmission to AEAT follows its course in batches.

## Response `200`

Late retry of an unattended checkout: the `external_id` already belongs to an invoice issued in your company, so that invoice is returned unchanged — with the `Idempotent-Replayed: true` header — after completing the VeriFactu alta and the payment if they were missing. Nothing new is created and the invoice number is not consumed again. If the type or the total of the request differ from the issued invoice, the answer is `409` `unattended_replay_mismatch` instead.

- object
  - `data` InvoiceWithCheckoutBlocks, required: An invoice (compliant with Spanish AEAT VeriFactu).
    - `id` string, required
    - `object` 'invoice', required
    - `number` string, nullable, required: Definitive invoice number, or `null` for drafts (where `is_number_assigned` is `false`).
    - `is_number_assigned` boolean, required: Whether the invoice has a definitive number assigned. `false` for drafts (where `number` is `null`); becomes `true` after `POST /v1/companies/{company}/invoices/{invoice}/assign-real-number`, or automatically when the invoice is issued.
    - `type` string, required: Document type: `F1` (ordinary invoice, *ordinaria*), `F2` (simplified invoice, *simplificada*), `F3` (invoice replacing simplified ones, *sustitutiva de simplificadas*) or `R5` (corrective of a simplified invoice). A corrective of an `F1`/`F3` keeps `F1` here; its AEAT corrective code (`R1`–`R5`) is `corrective.correction_aeat_type`, and `is_corrective` tells correctives apart.
    - `series` SeriesRef, required
      - `id` string, nullable, required
      - `code` string, nullable, required
    - `client` ClientRef, required
      - `id` string, nullable, required
      - `name` string, nullable, required
    - `status` 'draft' | 'scheduled' | 'issued' | 'paid' | 'partially_paid' | 'overdue' | 'cancelled' | 'annulled' | 'sent', required: Public invoice status. `issued` means the invoice has been ISSUED (definitive number, VeriFactu record); it says nothing about delivery, which lives in `is_sent`/`sent_at`/`sent_via`. `paid` and `partially_paid` are derived from the payment ledger. Before API version `2026-10-01` the issued status is published as `sent` (that contract called issuing "sending"), so `sent` only appears for integrations pinned to an earlier version.
    - `price_list_id` string, nullable, required: UUID of the price list selected for this document.
    - `price_list_name` string, nullable, required: Price-list name snapshot frozen on the document.
    - `issued_on` string, date, required
    - `due_on` string, date, nullable, required
    - `operation_on` string, date, nullable, required: Date the operation took place when it differs from `issued_on` (arts. 6.1.f and 7.1.c of Royal Decree 1619/2012), or `null` when both coincide. It cannot be later than `issued_on` unless the first line that declares a `regime_key` uses 14 or 15 (otherwise 422 `operation_date_after_issue_date`), and it is immutable once the invoice is issued. A corrective invoice inherits the date of the original. It is printed on the PDF and reported in the VeriFactu record (`FechaOperacion`).
    - `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 (4,320.00 + 259.20 − 648.00 = 3,931.20 against a real total of 4,579.20). It is NOT the VAT figure of the Spanish Modelo 303 — read `total_vat` for that. Beware that on an expense the same field name carries a DIFFERENT meaning (VAT only), which is why the identity that holds across all five document families is the explicit one: `total === subtotal + total_vat + total_surcharge − total_retention`.
    - `total_vat` string, required: Output VAT (IVA repercutido) accrued by the document: sum of the VAT of its 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 (retención de IRPF) applied to the document: sum of the withholding of its 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 document: sum of the surcharge of its lines. It ADDS to the total exactly like VAT does, and is ALREADY included inside `taxes_total`.
    - `total` string, required: Total of the document. Two equivalent ways to reconstruct it from the published amounts, and only these two: the EXPLICIT one, identical in the five 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 and yields a false total (4,320.00 + 259.20 - 648.00 = 3,931.20 against a real 4,579.20).
    - `total_disbursements` string, required: Sum of the `SUPLIDO` (disbursement) lines of this invoice: amounts the issuer paid in the name and on behalf of the client and re-invoices at cost. Deliberately OUTSIDE `subtotal`, `taxes_total` and `total`, because a disbursement is not part of the issuer's taxable base (art. 78.Tres.3 LIVA) and is not declared in the AEAT VeriFactu record. `0` on an invoice without disbursements.
    - `total_to_pay` string, required: Amount the client actually has to pay: `total + total_disbursements`. DERIVED, never stored — one single formula computes it — and equal to `total` on an invoice without disbursements. Worked example: a 1,000.00 service line at 21% plus a 150.00 `SUPLIDO` line yields `subtotal` 1000.00, `taxes_total` 210.00, `total` 1210.00, `total_disbursements` 150.00 and `total_to_pay` 1360.00. Note that `paid_amount`/`pending_amount` are measured against `total`, not against `total_to_pay`.
    - `currency` string, required: ISO 4217 currency code (always "EUR" in v1).
    - `notes` string, nullable, required
    - `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`.
    - `lines` InvoiceLine[], required
      - `object` 'invoice_line', required
      - `description` string, nullable, required
      - `additional_description` string, nullable, required: Optional secondary description rendered below the line concept on document PDFs.
      - `product` ProductRef, required
        - `id` string, required
        - `name` string, nullable, required
      - `quantity` number, required
      - `unit_price` string, required: Unit price as a decimal STRING with the 4-decimal scale of its column (e.g. `"0.0125"`, `"100.0000"`): it is returned exactly as it was accepted and stored, so reading a line and sending it back never changes its price. Derived amounts (`subtotal`, `taxes`, `total`) keep 2 decimals.
      - `tax_rate` number, required
      - `retention_rate` number, required: IRPF withholding percentage applied to the line (0–100). Default 0. Its amount is already aggregated into `taxes`.
      - `surcharge_rate` 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
      - `subtotal` string, required
      - `taxes` string, required
      - `total` string, required
      - `indirect_tax_regime` 'iva' | 'igic' | 'ipsi' | 'null', nullable: Indirect tax regime of the line: the per-document override (`iva`/`igic`/`ipsi`) when the user set it (precedence override>zone), otherwise `null` (derived from the establishment AEAT zone). Writable per-document input on create/update; the document must be homogeneous (a single non-null regime across all lines, 422 otherwise).
      - `variant` object, nullable, required
        - `id` string, required
        - `name` string, nullable, required
      - `presentation` object, nullable, required
        - `id` string, required
        - `name` string, nullable, required
        - `mode` string, nullable, required
      - `item_kind` string, nullable, required
      - `commercial_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
      - `base_unit_code` 'C62' | 'KGM' | 'GRM' | 'LTR' | 'MLT' | 'MTR' | 'MTK' | 'HUR' | 'DAY' | 'null', 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 resolved price: `per_base_unit` (product/variant own price, converted once by the presentation factor) or `per_commercial_unit` (presentation, combination or price-list entry, never converted). `null` on a line with no catalog price context.
      - `price_adjustment_total` string, nullable, required: Sum of the adjustments of the chosen option values, as a decimal string. Informational when an exact price already absorbed them.
      - `option_adjustments_absorbed` boolean, nullable, required: TRI-STATE, and `null` is NOT `false`. `true`: the resolved price already includes the option adjustments. `false`: they were added on top. `null`: it could not be determined — the assembler refuses such a line with 422 rather than risking charging the adjustments twice, so `null` only ever reaches a reader on a legacy line with no configurable options at all.
      - `regime_key` string, nullable: VeriFactu special regime key (ClaveRegimen, AEAT L8.1) declared for this line, or null when the line inherits the regime from the invoice header.
      - `exemption_reason` string, nullable: LIVA exemption cause (E1–E6) or non-subjection cause (N1/N2) declared for this line, or null when the line inherits the qualification from the invoice header.
      - `aeat_tax_code` string, nullable: AEAT operation qualification code snapshot frozen at issuance. Read-only (derived).
      - `line_type` 'NORMAL' | 'SUPLIDO': Kind of line. `NORMAL` is an ordinary line of the issuer's own operation. `SUPLIDO` is a DISBURSEMENT: an amount the issuer paid in the name and on behalf of the client (an official fee, duty or registry charge) and now re-invoices at cost. Under art. 78.Tres.3 of the Spanish VAT Act (LIVA) a disbursement is not part of the issuer's taxable base, so a `SUPLIDO` line stays out of `subtotal`, `taxes_total` and `total`, is aggregated into `total_disbursements` instead, carries no VAT, withholding, surcharge, discount or product, and is never declared in the AEAT VeriFactu record. Always present and never `null` in a response (a line created before this field existed reads as `NORMAL`); optional on write, where it defaults to `NORMAL`.
      - `source_invoice_reference` string, nullable: Reference of the supporting document that originated the disbursement — the receipt or fee number issued by the public body (≤100 chars). REQUIRED on a `SUPLIDO` line (a disbursement without its supporting reference is rejected with 422) and `null` on a normal line. Free text on purpose: the receipt of a public body is rarely a supplier invoice.
      - `source_invoice_ids` string[], nullable: Optional traceability of a `SUPLIDO` line: IDs (UUID v7) of your own expenses that back the disbursement. `null` when the line carries no traceability — never `[]`, so "no traceability" cannot be confused with "empty list". An expense of another company is rejected with 422.
      - `unit` string, nullable: Unit of measure printed next to the quantity on the document (`hours`, `kg`, `units`, …), ≤20 chars, or `null` when not reported. Presentation only: free text with no closed catalog and no fiscal effect, since no AEAT catalog applies to the ordinary Spanish invoice.
      - `exemption_reason_text` string, nullable: Free-text wording of the exemption provision of this line (≤255 chars), printed under the line description to satisfy the mention required by art. 6.1.j of Royal Decree 1619/2012 when the catalogued cause does not cover it. Orthogonal to `exemption_reason` and to the document-level exemption cause: no coherence is enforced between them and the automatic header legal mention is unchanged.
    - `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.
    - `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).
    - `operation_regime` string, required: AEAT VAT operation regime (`general`, `recargo_equivalencia`, `exenta`, etc.).
    - `exemption_reason` string, nullable, required: VAT exemption cause per document (AEAT catalog E1..E6), or `null` when not exempt. Read-only: the public API derives the AEAT qualification from the regime/cause at issuance.
    - `legal_mentions` string[], required: Header legal mentions (includes the mention derived from the exemption cause). Read-only.
    - `exclude_347` boolean, required: Read-only flag: whether this invoice is excluded from the annual Modelo 347 report. The public API cannot change it; the flag is managed from the Factuarea app.
    - `verifactu_status` string, required: Status of the AEAT VeriFactu submission.
    - `paid_amount` string, required: Amount already collected for this invoice (derived from the payment ledger). Satisfies the invariant `paid_amount + pending_amount === total`.
    - `pending_amount` string, required: Outstanding balance pending collection for this invoice (derived from the payment ledger).
    - `payments` object, required: Payment ledger summary, ALWAYS present (never `null`). `total` mirrors `paid_amount`, `pending` mirrors `pending_amount`. `detail` lists the individual payments and is materialized ONLY on the show endpoint (`GET /v1/companies/{company}/invoices/{invoice}`); in list responses `detail` is `[]` (by cost) while `total`/`pending` stay populated. The detail is also available via `GET /v1/companies/{company}/invoices/{invoice}/payments`.
      - `detail` InvoicePaymentDetail[], required: List of individual payments. Empty `[]` in list responses or when there are no payments yet.
        - `id` string, required: Opaque UUID v7 of the payment.
        - `object` 'payment', required: String identifying the object type. Always `payment`.
        - `invoice_id` string, required: UUID v7 of the invoice this payment belongs to.
        - `amount` string, required: Payment amount.
        - `payment_date` string, date, required: Payment date (YYYY-MM-DD).
        - `payment_method` string, required: Payment method (backing value of the Shared enum `PaymentMethod`, e.g. `bank_transfer`).
        - `payment_method_text` string, required: Human-readable label of the payment method (Spanish).
        - `reference` string, nullable, required: Payment reference/operation number. `null` if not provided.
        - `notes` string, nullable, required: Internal notes for the payment. `null` if not provided.
        - `is_reversed` boolean, required: Whether the payment has been reverted. A reverted payment stays in the ledger but stops counting towards `paid_amount`, `pending_amount` and every treasury aggregate — read this flag, do not infer the state from absence.
        - `reversed_at` string, date-time, nullable, required: When the payment was reverted (ISO 8601), or `null` while it is in force.
        - `reversal_reason` 'direct_debit_return' | 'card_dispute' | 'misapplied_payment' | 'bounced_effect' | 'recording_error' | 'issued_in_error' | 'null', nullable, required: Reason the payment was reverted, from the closed catalog. `issued_in_error` appears only on payments reverted by annulling an invoice issued by mistake with `revert_collections`; it cannot be requested for a single payment. `null` while the payment is in force.
        - `reversal_reason_text` string, nullable, required: Human-readable label of the reversal reason (Spanish), or `null`.
        - `reversal_note` string, nullable, required: Free-text remark recorded with the reversal (up to 500 characters), or `null`.
        - `created_at` string, date-time, nullable, required: When the payment was recorded (ISO 8601), or `null`.
        - `updated_at` string, date-time, nullable, required: When the payment was last updated (ISO 8601), or `null`.
      - `total` string, required: Total amount collected (mirrors `paid_amount`).
      - `pending` string, required: Outstanding balance pending collection (mirrors `pending_amount`).
    - `is_corrective` boolean, required: Whether this invoice is a corrective (rectificativa) of another invoice.
    - `corrective` InvoiceCorrective, required: Correction data of a corrective invoice. Present (object) when `is_corrective=true`, `null` on ordinary invoices.
      - `original_id` string, nullable, required: UUID v7 of the original corrected invoice.
      - `original_number` string, nullable, required: Human-readable number of the original corrected invoice.
      - `original_date` string, date, nullable, required: Issue date of the original invoice (YYYY-MM-DD).
      - `correction_reason` string, nullable, required: Reason for the correction (AEAT code R1–R5).
      - `correction_type` string, nullable, required: Scope of the correction: `total` (the whole invoice, `correction_type: full` on input) or `partial`.
      - `correction_nature` string, nullable, required: Effective nature of the correction (art. 15.5 RD 1619/2012), the same one its VeriFactu record declares as `TipoRectificativa`: `I` by differences (the corrective carries the amount of the rectification, any sign) or `S` substitution (the invoice as it ends up). A corrective with a negative base is always `I`.
      - `base_rectificada` string, nullable, required: Taxable base of the ORIGINAL invoice being corrected; reported to VeriFactu as `ImporteRectificacion.BaseRectificada` on a substitution (`S`).
      - `cuota_rectificada` string, nullable, required: VAT amount of the ORIGINAL invoice being corrected; reported to VeriFactu as `ImporteRectificacion.CuotaRectificada` on a substitution (`S`).
      - `correction_aeat_type` 'R1' | 'R2' | 'R3' | 'R4' | 'R5' | 'null', nullable, required: AEAT corrective invoice type code (`R1`–`R5`) frozen at issuance from the VeriFactu record. `null` if not resolved.
    - `payment` InvoicePayment, required: Payment data of the invoice. An object when `status` is `paid`, `null` otherwise.
      - `method` string, nullable, required: Payment method (backing value of the Shared enum `PaymentMethod`, e.g. `bank_transfer`).
      - `reference` string, nullable, required: Payment reference (e.g. transfer number).
      - `date` string, date, nullable, required: Payment date (YYYY-MM-DD).
    - `public_link` PublicLink, required: Represents the state of the shareable public link of a document (quote/invoice/proforma/delivery_note). `url` is the absolute URL ready to send to the client; `enabled` indicates whether it is active; `expires_at` the deadline (`null` = unlimited); `max_days` the maximum allowed when extending it.
      - `object` 'public_link', required
      - `url` string, uri, required: Absolute URL of the public link to share with the client.
      - `id` string, required: UUID v7 of the document the link points to.
      - `enabled` boolean, required: Indicates whether the public link is currently active.
      - `expires_at` string, date-time, nullable, required: Expiration date/time of the link, or `null` if it does not expire.
      - `max_days` integer, required: Maximum number of days allowed when extending the link validity (business limit).
    - `substituted_by` InvoiceSubstitutedBy, required: Reference to the full invoice (F3) that replaced this simplified invoice (F2). `null` when not applicable.
      - `id` string, nullable, required: UUID v7 of the substitute full invoice.
      - `number` string, nullable, required: Human-readable number of the replacement full invoice.
    - `recurring` InvoiceRecurring, required: Reference to the recurring invoice (template) that generated this invoice. `null` when the invoice does not come from a recurrence.
      - `id` string, required: UUID v7 of the parent recurring invoice.
    - `paid_recorded_at` string, date-time, nullable, required: Instant (ISO 8601 with offset) at which the collection that settled the invoice was RECORDED in the ledger. `null` while the invoice is not collected. Not the declared date: see `paid_date`.
    - `paid_date` string, date, nullable, required: Declared collection date (YYYY-MM-DD). `null` while the invoice is not collected. The input field of `mark-paid` and `payments` keeps the name `paid_on`.
    - `issued_at` string, date-time, nullable, required: When the invoice was issued (ISO 8601), or `null` while it is a draft or scheduled. Not present before API version `2026-10-01`: earlier versions publish this instant in `sent_at`.
    - `sent_at` string, date-time, nullable, required: When the invoice was first delivered to the customer (ISO 8601): the moment the mail server accepted its delivery email, or the date of a manual mark. `null` while it has not been delivered. A resend never moves it. Before API version `2026-10-01` this field carries the ISSUANCE instant instead (that contract called issuing "sending").
    - `sent_via` 'email' | 'manual' | 'null', nullable, required: Channel of the first delivery: `email` (the mail server accepted the delivery email) or `manual` (marked with `POST /v1/companies/{company}/invoices/{invoice}/mark-sent`). `null` while the invoice has not been delivered. Not present before API version `2026-10-01`.
    - `is_sent` boolean, required: Whether the invoice has been delivered to the customer, independently of its fiscal `status`: an issued or overdue invoice can be delivered or not. Derived from the delivery mark (`sent_at` + `sent_via`), never set directly. Not present before API version `2026-10-01`.
    - `voided_at` string, date-time, nullable, required
    - `void_reason` string, nullable, required
    - `scheduled_for` string, date-time, nullable, required: Scheduled emission timestamp (ISO 8601), or `null` when the invoice is not scheduled. Populated only while `status` is `scheduled`.
    - `scheduled_action` 'issue' | 'issue_and_send' | 'draft' | 'null', nullable, required: Action the scheduler runs when `scheduled_for` is reached: `issue` (issue without sending) or `issue_and_send` (issue and email it). `null` when the invoice is not scheduled. Before API version `2026-10-01` the `issue` action is published as `draft`, its previous name.
    - `channel` string, nullable, required: Sales channel the invoice originated from (`woocommerce`, `shopify`, `prestashop`), or `null` when it was not created from a store order — which is the common case.
    - `source_store_id` string, uuid, nullable, required: UUID v7 of the connected store whose order produced this invoice, or `null` when it was not created from a store order. A store order is not a resource of its own on this API, so `channel` and `source_store_id` are the traceability from the order to the invoice.
    - `origin` 'native' | 'historical_import', required: Where the invoice comes from: `native` (issued in Factuarea) or `historical_import` (a historical invoice registered by import: literal number from the file, no series counter and no VeriFactu record).
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `verifactu` object, nullable: State of the VeriFactu registration (alta) generated by the issuance, ready to print on the receipt. `null` when the alta was not requested and the invoice has no record (an `F2` or a payment without `options.register_verifactu`): the terminal tells "no alta" apart from "alta failed". The alta is generated BEFORE responding; its transmission to AEAT follows its course in batches afterwards.
      - `status` 'registered' | 'failed', required: `registered`: the alta exists. `failed`: the invoice is issued but the alta could not be generated — `error_code` says why and a retry with the same `external_id` completes it.
      - `error_code` 'certificate_missing' | 'certificate_expired' | 'certificate_revoked' | 'certificate_nif_mismatch' | 'clock_drift_exceeded' | 'representation_required' | 'system_certificate_unavailable' | 'verifactu_not_enabled' | 'null', nullable, required: Why the alta failed. `null` unless `status` is `failed`.
      - `aeat_status` string, nullable, required: Status of the record before AEAT (`pending`, `submitted`, `accepted`, `rejected` or `error`), or `null` when there is no record.
      - `huella` string, nullable, required: SHA-256 fingerprint of the record (64 hexadecimal characters), or `null` while it does not exist.
      - `qr_url` string, uri, nullable, required: AEAT verification URL carried by the QR, with its four parameters (`nif`, `numserie`, `fecha`, `importe`), or `null`.
      - `qr_png_base64` string, nullable, required: The QR as a PNG image, base64-encoded without a data-URI prefix (error-correction level M), ready to print, or `null`.
      - `legend` string, nullable, required: Legend to print next to the QR, with the wording AEAT requires: `VERI*FACTU` when the platform operates in VERI*FACTU mode (the usual case) or `Factura verificable en la sede electrónica de la AEAT` in NO VERI*FACTU mode. `null` when there is no QR to print (for example when the alta could not be generated).
      - `csv` string, nullable, required: AEAT secure verification code (CSV). `null` until AEAT accepts the submission.
    - `pdf` object, nullable: The A4 PDF of the invoice. `null` while the invoice is still a draft (it has no PDF yet). For another paper — 80 or 58 mm tickets — call `GET /v1/companies/{company}/invoices/{invoice}/pdf?format=ticket_80|ticket_58`, which renders in the request.
      - `status` 'ready' | 'pending', required: `ready`: the PDF is already in the private cache and `url` serves it at once (only with `options.wait_for_pdf`, which enqueues the render and waits up to about 15 s). `pending`: the render was enqueued and `url` answers 404 until the worker materializes it (seconds) — also the state of a `wait_for_pdf` whose wait expired. It is never an error.
      - `url` string, uri, required: Signed, temporary URL to the PDF. It needs no API key, only opens this invoice and stops working at `expires_at`.
      - `expires_at` string, date-time, required: Instant after which the signed `url` stops working.
    - `public_url` string, uri, nullable: Public link of the invoice (`/d/{id}`) from which the end customer downloads it — the same as `public_link.url`. `null` while the invoice is a draft or when its public link is disabled.

## Other responses

- `201`: Invoice created successfully. In an unattended checkout (`type: F2`, a `payment` block or `options.register_verifactu`) the body also carries the `verifactu`, `pdf` and `public_url` blocks. The `Location` header contains the canonical URL of the newly created resource.
- `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.
- `402`: The operation requires a payment that could not be completed: either no payment method is on file (`error.details.payment_setup_url` links to the Billing Portal where it can be set up), the immediate charge was declined by the payment provider, the account lacks the plan or add-on this operation bills against, the annual document limit is reached (`plan_limit_exceeded`, with `error.details.period_end` indicating the end of the period — wait until it has ended or upgrade the plan to continue now), or the account has reached its maximum usage (`storage_quota_exceeded`, raised by upload operations such as signing a delivery note or attaching a file to an expense — contact support before retrying). Nothing was created or modified — resolve the cause and retry the same request. Version note: `error.type` is `payment_required_error` from `Factuarea-Version: 2026-09-01` onwards; earlier versions receive `invalid_request_error` for the three codes that predate that cut (`payment_method_required`, `employee_seat_payment_method_required` and `employee_seat_charge_failed`; every other 402 code, such as `addon_required`, `plan_limit_exceeded` or the additional tax ID codes, is newer and always carries `payment_required_error`). `error.code` is stable across every version.
- `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. `profile_not_found`: the company in the path does not exist, is not in the API key scope or belongs to the other environment (live or test).
- `409`: The request conflicts with data that already exists or with a request running at the same time. `resource_locked`: another request with the same `external_id` is still being processed (up to 20 seconds). Nothing was written by this request; send the same request again, which will return the invoice already issued. `unattended_replay_mismatch`: the `external_id` already belongs to an issued invoice with a different type or total. It identifies ONE operation: use a new `external_id` for a new operation. Nothing was changed. `resource_locked` with `subcode` `chain_position_conflict`: another process was generating VeriFactu billing records for the company at the same time and this one could not take its place in the chain after several attempts. Nothing was written; send the request again. `resource_conflict`: another request was reserving identifiers for the same kind of record at that moment and this one could not complete its reservation. Nothing was written; send the request again after a few seconds.
- `422`: Validation failed, or the invoice cannot undergo the requested state transition (e.g. marking an already-paid invoice as paid, or editing an issued invoice — use a corrective instead). The `error.param` field identifies which input is invalid, if any. Issuing or annulling an invoice can also be rejected with `verifactu_not_eligible`: either a field of the invoice does not fit its billing record, or — only when the company has VeriFactu enabled in NO VERI*FACTU mode — it has no usable signing certificate (`error.subcode: signing_certificate_unavailable`; `error.param` is `certificate`, `representation` or `system_certificate`, depending on what is missing); in both cases the invoice stays exactly as it was.
- `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)
