> ## Documentation Index
> Fetch the complete documentation index at: https://docs.printools.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Update a quote line

> Requires the scope quotes.write.

Required scopes: `quotes.write`.


## OpenAPI

````yaml PATCH /v1/quote-lines/{lineId}
openapi: 3.1.0
info:
  title: PRINTOOLS API
  version: 1.0.0
  description: >-
    The PRINTOOLS public REST API. This document is generated from the live
    route table and service GraphQL SDL; do not edit it by hand.


    Every 2xx response has {status, message, data}. Every non-2xx response has
    {error, message, status: false}.


    Tenant credential: send X-API-Key only. It is bound to one shop.
    X-PrintTools-Organization-Id is optional for that credential and, if sent,
    must match its shop.


    Partner credential: send X-API-Key, X-API-Secret, Authorization: Bearer
    <Cognito access token>, and X-PrintTools-Organization-Id. The OAuth flow is
    documented at https://app.printools.io/api/index.html#partner-auth.


    Read live limits from GET /v1/reference/limits. Field shapes are projected
    from operation declarations; local endpoints have explicit handler-derived
    schemas.
servers:
  - url: https://api.printools.io
    description: Production
security: []
externalDocs:
  description: Getting started, OAuth and webhook guidance
  url: https://app.printools.io/api/index.html
paths:
  /v1/quote-lines/{lineId}:
    patch:
      tags:
        - quote-lines
      summary: Update a quote line.
      description: Requires the scope quotes.write.
      operationId: patch_v1_quote_lines_lineId
      parameters:
        - name: lineId
          in: path
          required: true
          schema:
            type: string
            format: uuid
            example: 11111111-2222-3333-4444-555555555555
          description: UUID.
        - name: X-PrintTools-Organization-Id
          in: header
          required: false
          schema:
            type: string
            format: uuid
          description: >-
            Tenant credential: optional and, when sent, must equal the shop
            bound to the key. Partner credential: required; it selects an
            organisation the user previously authorised.
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            type: string
            minLength: 1
            maxLength: 200
          description: >-
            Optional client-generated key for write retries. Reusing it with a
            different request returns 409; a replay includes
            Idempotency-Replayed: true.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                lineType:
                  $ref: '#/components/schemas/Enum_QuoteLineType'
                garmentCatalogueItemId:
                  type: string
                productName:
                  type: string
                productSku:
                  type: string
                title:
                  type: string
                description:
                  type: string
                quantity:
                  type: integer
                unitPriceExGst:
                  type: number
                valueType:
                  $ref: '#/components/schemas/Enum_LineValueType'
                percentage:
                  type: number
                discountValueType:
                  $ref: '#/components/schemas/Enum_DiscountValueType'
                  description: >-
                    Deprecated alias of valueType (F-26). valueType wins when
                    both are sent.
                discountPercentage:
                  type: number
                  description: >-
                    Deprecated alias of percentage (F-26). percentage wins when
                    both are sent.
                sortOrder:
                  type: integer
                customImageUrl:
                  type: string
                variants:
                  type: array
                  items:
                    $ref: '#/components/schemas/Input_QuoteVariantInput'
            examples:
              example:
                summary: Replace example values with a request for your shop.
                value:
                  lineType: Garment
                  garmentCatalogueItemId: example
                  productName: example
                  productSku: example
                  title: example
      responses:
        '200':
          description: Successful response.
          headers:
            Idempotency-Replayed:
              description: >-
                Present and true when this response was replayed from a prior
                Idempotency-Key request.
              schema:
                type: string
                enum:
                  - 'true'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Success'
                  - type: object
                    properties:
                      data:
                        $ref: '#/components/schemas/Output_QuoteLine'
        '400':
          description: >-
            Validation or request-shape error. Correct the request before
            retrying.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Credential authentication failed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            WWW-Authenticate:
              schema:
                type: string
                example: ApiKey
        '403':
          description: >-
            The credential lacks a required scope, or its actor lacks
            permission.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: The resource or endpoint was not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: >-
            Conflict, including idempotency-key reuse or an in-flight idempotent
            request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: The JSON request body exceeds the published size limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited. Retry after the returned delay.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            Retry-After:
              description: Whole seconds to wait.
              schema:
                type: integer
                minimum: 0
        '500':
          description: An unexpected server error. A retry can be appropriate.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - tenantApiKey: []
        - partnerApiKey: []
          partnerApiSecret: []
          partnerBearer: []
      x-codeSamples:
        - lang: curl
          label: Tenant credential
          source: >-
            curl --request PATCH
            'https://api.printools.io/v1/quote-lines/11111111-2222-3333-4444-555555555555'
            \
              --header 'X-API-Key: $PRINTOOLS_API_KEY' \
              --header 'Content-Type: application/json' \
              --data '{"lineType":"Garment","garmentCatalogueItemId":"example","productName":"example","productSku":"example","title":"example"}'
components:
  schemas:
    Enum_QuoteLineType:
      type: string
      enum:
        - Garment
        - Decoration
        - Service
        - Cost
        - Discount
        - Surcharge
        - Other
    Enum_LineValueType:
      type: string
      enum:
        - Flat
        - Percentage
    Enum_DiscountValueType:
      type: string
      enum:
        - Flat
        - Percentage
    Input_QuoteVariantInput:
      type: object
      properties:
        size:
          type: string
        colour:
          type: string
        quantity:
          type: integer
        unitPriceExGst:
          type: number
        buyPrice:
          type: number
        extraCost:
          type: number
        markupType:
          $ref: '#/components/schemas/Enum_MarkupType'
        markupValue:
          type: number
        markupSource:
          $ref: '#/components/schemas/Enum_MarkupSource'
          description: >-
            Omitted → `manual`, the safe answer for any client that does not
            know about

            ownership yet. A client that let a group resolve the markup must
            send

            `group` EXPLICITLY (CU-86d46hu37 AC 12).
        isCustomLine:
          type: boolean
        showOnDocuments:
          type: boolean
          description: Omitted → rolled up, the default for every custom variant (QU-02).
        sortOrder:
          type: integer
      required:
        - quantity
    Success:
      type: object
      required:
        - status
        - message
        - data
      properties:
        status:
          type: boolean
          description: >-
            The actual resolver result. It can be false when an operation
            returns false or null without throwing.
        message:
          type: string
          example: OK
        data: {}
      description: Every 2xx response uses this envelope.
    Output_QuoteLine:
      type: object
      properties:
        id:
          type: string
        quoteGroupId:
          type: string
        lineType:
          $ref: '#/components/schemas/Enum_QuoteLineType'
        garmentCatalogueItemId:
          type:
            - string
            - 'null'
        productName:
          type: string
        productSku:
          type:
            - string
            - 'null'
        title:
          type:
            - string
            - 'null'
        description:
          type:
            - string
            - 'null'
        quantity:
          type: integer
        unitPriceExGst:
          type: number
        valueType:
          anyOf:
            - $ref: '#/components/schemas/Enum_LineValueType'
            - type: 'null'
          description: >-
            Flat or Percentage. Applies to a Discount AND (F-26) a Surcharge — a
            rate on

            a line, whichever direction it points.
        percentage:
          type:
            - number
            - 'null'
          description: The rate, when valueType is Percentage. Null otherwise.
        discountValueType:
          anyOf:
            - $ref: '#/components/schemas/Enum_DiscountValueType'
            - type: 'null'
        discountPercentage:
          type:
            - number
            - 'null'
        customImageUrl:
          type:
            - string
            - 'null'
          description: Per-line image override; null falls back to the catalogue thumbnail.
        sortOrder:
          type: integer
          description: >-
            Position in the group's row sequence — shared with the group's
            DECORATIONS

            since F-26, so a surcharge or discount can sit below a decoration.
            Omit on

            create to append.
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        variants:
          type: array
          items:
            $ref: '#/components/schemas/Output_QuoteVariant'
      required:
        - id
        - quoteGroupId
        - lineType
        - productName
        - quantity
        - unitPriceExGst
        - sortOrder
        - createdAt
        - updatedAt
        - variants
    Error:
      type: object
      required:
        - error
        - message
        - status
      properties:
        error:
          type: string
          description: Stable machine-readable error code.
        message:
          type: string
        status:
          type: boolean
          const: false
      description: Every non-2xx response uses this envelope.
    Enum_MarkupType:
      type: string
      enum:
        - percentage
        - flat_rate
    Enum_MarkupSource:
      type: string
      description: >-
        WHO owns a markup, and therefore whether it may be re-resolved through
        its

        markup group's matrix (CU-86d46hu37).


        `group` — a markup group resolved the number, so it re-bands on every
        quantity

        or cost change, on create and on reopen alike; the sell price moves with
        it.

        `manual` — an operator typed the markup, flipped its \$/% toggle, or
        overrode

        the sell price. Never re-banded, and a cost edit back-solves to hold the
        saved

        sell fixed, which is the edit-lock that stops a live quote repricing
        itself.


        Stored `not null default 'manual'`, so every row written before this
        existed is

        operator-owned and behaves exactly as it did. A create path that means
        `group`

        must SAY `group` — relying on the default silently reintroduces the bug
        the

        enum exists to fix.
      enum:
        - group
        - manual
    Output_QuoteVariant:
      type: object
      properties:
        id:
          type: string
        quoteLineId:
          type: string
        size:
          type:
            - string
            - 'null'
        colour:
          type:
            - string
            - 'null'
        quantity:
          type: integer
        unitPriceExGst:
          type: number
        buyPrice:
          type: number
          description: Present only when the credential includes the costs.read scope.
        extraCost:
          type: number
          description: Present only when the credential includes the costs.read scope.
        markupType:
          anyOf:
            - $ref: '#/components/schemas/Enum_MarkupType'
            - type: 'null'
          description: >-
            The markup that connects cost to sell, stored so it survives a
            reopen

            (BE-01 / Q-16). Cost is `buyPrice + extraCost`; sell is

            `percentage ? cost + cost·value/100 : cost + value`. Null on rows
            written

            before the column existed — read those back by back-solving from
            cost and

            sell, as the client did for every row previously. `MarkupType` is
            the enum

            catalog-service already declares.
        markupValue:
          type:
            - number
            - 'null'
        markupSource:
          $ref: '#/components/schemas/Enum_MarkupSource'
          description: >-
            Whether the markup above belongs to a markup GROUP or to the
            operator

            (CU-86d46hu37). `group` re-resolves through the group's matrix
            whenever the

            quantity or the cost moves — on reopen as much as on create — while
            `manual`

            is left alone and holds its saved sell price through a cost edit.


            Never null: rows written before the column existed read `manual`,
            which is

            precisely how they behaved.
        isCustomLine:
          type: boolean
          description: >-
            A cost that belongs to the line but is not per-garment — freight is
            the

            example (BE-02 / Q-14). Stored as a variant with no size and no
            colour; the

            flag is what tells one from a half-filled variant, since the size
            sort, the

            quote document and the PDF all walk these rows.
        showOnDocuments:
          type: boolean
          description: >-
            Whether a CUSTOM variant is itemised on the documents the customer
            sees —

            quote, invoice, PDF, portal (QU-02).


            False, the default, folds its money into the garment line's total
            and draws

            no row anywhere. True prints one row reading `Custom variant`,
            quantity 1,

            with size and colour blank. Either way the money is identical: a
            line's

            total already sums every variant beneath it, custom rows included.


            Ignored on a real size/colour variant — that row IS the itemisation.
        sortOrder:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
      required:
        - id
        - quoteLineId
        - quantity
        - unitPriceExGst
        - markupSource
        - isCustomLine
        - showOnDocuments
        - sortOrder
        - createdAt
        - updatedAt
  securitySchemes:
    tenantApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: Tenant credential. This is sufficient by itself.
    partnerApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: Partner application client ID.
    partnerApiSecret:
      type: apiKey
      in: header
      name: X-API-Secret
      description: Partner application secret.
    partnerBearer:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Cognito access token obtained through OAuth.

````