---
title: "Retrieve a proforma"
method: GET
path: "/companies/{company}/proformas/{proforma}"
tags: ["Proformas"]
---

# Retrieve a proforma

`GET /companies/{company}/proformas/{proforma}`

## Request

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

Retrieve a proforma invoice by its `id`.

## Path parameters

- `company` string, uuid, required
- `proforma` string, uuid, required

## Headers

- `Factuarea-Version` string, date

## Response `200`

- object
  - `data` Proforma, required: A proforma invoice that can be converted to a final invoice.
    - `id` string, required
    - `object` 'proforma', required
    - `number` string, required
    - `series` SeriesRef, required
      - `id` string, nullable, required
      - `code` string, nullable, required
    - `client` ClientRef, required
      - `id` string, nullable, required
      - `name` string, nullable, required
    - `status` string, required: Proforma lifecycle status (draft, accepted, rejected, cancelled, expired, converted).
    - `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
    - `valid_until` string, date, nullable, required
    - `validity_days` integer, nullable, required: Number of validity days of the proforma since its issuance. `null` if not applicable.
    - `reference` string, nullable, required: Free reference of the document (e.g. the customer order number).
    - `operation_regime` string, required: VAT operation regime of the proforma (`general`, `intracomunitaria`, `importacion_exportacion`, `isp`).
    - `converted_to_id` string, nullable, required: UUID v7 of the invoice this proforma was converted into, if applicable.
    - `converted_invoice_number` string, nullable, required: Human-readable number of the invoice this proforma was converted into (e.g. "F-2026-00042"). `null` if not converted.
    - `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: Sum of the VAT of all lines.
    - `total_retention` string, required: Sum of the withholding (IRPF) of all lines.
    - `total_surcharge` string, required: Sum of the equivalence surcharge of all lines.
    - `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).
    - `shipping_cost` string, required: Shipping cost, expressed WITH VAT INCLUDED, added to the amount to pay. It is not part of `subtotal`, `total_vat` or `total`. When the proforma is converted to an invoice it becomes a "Gastos de envío" line whose base is `shipping_cost / (1 + r)` with VAT `r`, so the invoice total equals `total_with_shipping`.
    - `total_with_shipping` string, required: Final total including the shipping cost (= total + shipping_cost). The invoice generated from the proforma has exactly this total.
    - `currency` string, required
    - `payment_method` string, nullable, required: Preferred payment method (backing value of the Shared enum `PaymentMethod`, e.g. `bank_transfer`).
    - `payment_terms_days` integer, nullable, required: Payment term in days (Net X). Valid range: 0-365.
    - `delivery_terms` string, nullable, required: Delivery terms, free-form text.
    - `estimated_delivery_date` string, date, nullable, required: Estimated delivery date (YYYY-MM-DD).
    - `notes` string, nullable, required
    - `terms_and_conditions` string, nullable, required: Terms and conditions rendered in the proforma PDF.
    - `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` ProformaLine[], required
      - `object` 'proforma_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.
    - `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).
    - `link_expires_at` string, date-time, nullable, required: Expiration date of the shareable public link, or `null` if unlimited.
    - `link_is_active` boolean, required: Indicates whether the shareable public link is currently active.
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required

## Other responses

- `400`: `parameter_unknown` with `subcode` `retired_active_profile_header`: the retired `X-Active-Profile` header was sent; the company goes in the path. `parameter_invalid_format` on `Factuarea-Version`: the header is not a `YYYY-MM-DD` date. `unsupported_api_version`: the `Factuarea-Version` date is not a supported version.
- `401`: Missing or invalid API key.
- `403`: The request is authenticated but not allowed: the API key lacks the required scope (`insufficient_scope`), the plan does not include the public API or this feature (`addon_not_active`, `feature_not_available_in_plan`), or the module is not available with a test key (`module_not_available_in_sandbox`).
- `404`: The requested resource does not exist or belongs to another company.
- `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)
