---
title: "Update a quote"
method: PATCH
path: "/companies/{company}/quotes/{quote}"
tags: ["Quotes"]
---

# Update a quote

`PATCH /companies/{company}/quotes/{quote}`

## Request

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

Update a draft quote. Once accepted/rejected/converted, the quote becomes immutable.

## Path parameters

- `company` string, uuid, required
- `quote` string, uuid, required

## Headers

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

## Request body

- UpdateQuoteRequest: Partial update of a quote: omitted fields are kept. Only allowed while the quote is in `draft` status (otherwise 422 `invalid_status_transition`).
  - `client_id` string, uuid
  - `series_id` string, uuid, nullable
  - `price_list_id` string, uuid, nullable
  - `reprice_strategy` 'existing_catalog_lines' | 'future_lines_only'
  - `issued_on` string, date
  - `valid_until` string, date
  - `notes` string, nullable
  - `terms` 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[]
    - `description` string, nullable
    - `additional_description` string, nullable
    - `quantity` number
    - `unit_price` union: Unit price. Send a JSON number or a decimal string with up to 4 decimals (`"0.0125"`, `"100.0000"`): the string is the exact form the response returns, so a line read from the API can be sent back unchanged. Derived amounts are computed from it and keep 2 decimals.
      - number
      - string
    - `tax_rate_id` string, uuid, nullable
    - `tax_rate` number, nullable: VAT rate of the line (0–100). Optional, and it matters whenever `lines` is sent, because a `PUT` replaces the whole set of lines: a line that omits it (or sends `null`) takes the tax it references or the tax assigned to its product and otherwise the DEFAULT TAX of your company for this kind of document, inherited as a whole: its rate, its indirect-tax regime (IVA, IGIC or IPSI) and its AEAT qualification; an exempt or not-subject default also declares its cause (`exemption_reason`) on the line unless the line sends its own. If none of them exists the request is rejected with 422 `missing_required_param`, with `error.param` = `lines.N.tax_rate` and `error.line_index` = N (the zero-based index of the line); a rate already stored on a line is NOT kept if you resend the line without it. A `0` you send is honoured as a real 0 % rate.
    - `retention_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
    - `indirect_tax_regime` 'iva' | 'igic' | 'ipsi' | 'null', nullable
    - `configuration_uuid` string, uuid, nullable
    - `counted_quantity` number, nullable
    - `options` object[], nullable
      - `group_uuid` string, uuid, required
      - `value_uuid` string, uuid, required
    - `configuration_signature` string, nullable
    - `configuration_name` string, nullable
    - `price_source` 'price_list' | 'configuration' | 'presentation' | 'variant' | 'product' | 'manual' | 'supplier_offer' | 'pack_snapshot' | 'null', nullable
    - `price_semantics` 'per_base_unit' | 'per_commercial_unit' | 'null', nullable
    - `price_adjustment_total` number, nullable
    - `option_adjustments_absorbed` boolean, nullable

## Response `200`

- object
  - `data` Quote, required: A sales quote that can be converted to an invoice.
    - `id` string, required
    - `object` 'quote', 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: Quote lifecycle status (draft, sent, accepted, rejected, expired, converted, cancelled).
    - `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
    - `accepted_at` string, date-time, nullable, required
    - `rejected_at` string, date-time, nullable, required
    - `converted_to_id` string, nullable, required: UUID v7 of the invoice this quote was converted into, if any.
    - `converted_invoice_number` string, nullable, required: Human-readable number of the invoice this quote 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 VAT (IVA) across all lines.
    - `total_retention` string, required: Sum of withholding (IRPF/retention) across all lines.
    - `total_surcharge` string, required: Sum of equivalence surcharge (recargo de equivalencia) across 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).
    - `currency` string, required
    - `notes` string, nullable, required
    - `terms` string, nullable, required: Free-text terms and conditions rendered on the quote PDF.
    - `reference` string, nullable, required: Origin number of the document (e.g. the number it had in a previous system when imported). `null` if not set.
    - `operation_regime` string, required: VAT operation regime of the quote (`general`, `intracomunitaria`, `importacion_exportacion`, `isp`).
    - `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` QuoteLine[], required
      - `object` 'quote_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: Expiry date of the public share link, or null if unlimited.
    - `link_is_active` boolean, required: Whether the public share 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.
- `409`: The request conflicts with data that already exists or with a request running at the same time. `resource_already_exists` with `subcode` `external_id_already_exists`: another quote of the company already uses that `external_id`; `details.existing_resource_id` is its identifier.
- `422`: Validation failed, or the quote cannot undergo the requested state transition (e.g. accepting an already-accepted quote, or converting a non-accepted quote). The `error.param` field identifies which input is invalid, if any.
- `429`: Rate limit exceeded. Retry after the duration in `Retry-After`.
- `500`: Unexpected server error.

---

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