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

> ## Agent Instructions
> For new direct REST integrations, use the v2026-04 surfaces. The @fluid-app FairShare SDK continues to use its own published public-v2025-06 contract.
> Authenticate with the header Authorization: Bearer <token>; public storefront read endpoints require no auth.
> Lists use cursor pagination via the page[cursor] and page[limit] query params; follow meta.pagination.next_cursor until it is null.
> Never use /api/company/v1 or /api/v1 paths, page/per_page params, or offset pagination — they are legacy.
> The OpenAPI specs under api-reference/ are the authoritative contracts; prefer them over prose when in doubt. api-reference/storefront-v2026-04.yaml covers the v2026-04 storefront surface (/api/v202604/... paths); api-reference/auth-v0.yaml covers the unversioned auth surface (/api/... paths — authentication, MFA, social auth, and token exchange); api-reference/checkout-v2026-04.yaml covers the v2026-04 checkout surface (/api/checkout/v2026-04/... paths — carts, cart auth, discounts, items, subscriptions, orders, enrollments, and store config); api-reference/public-v2025-06.yaml covers the Public SDK surface used by the @fluid-app FairShare SDK, including its parallel cart lifecycle, browser integrations, versioned payment callbacks, unversioned public utilities, and the cart price-override operation; api-reference/payment-v2026-04.yaml covers the v2026-04 payment gateway admin surface (/api/payment/v2026-04/... paths, bearer-authenticated — gateway CRUD, gateway purchase/authorize/$0-verify, transaction list/show and capture/void/credit, and merchant payment configuration); api-reference/payments-v2026-04.yaml covers the v2026-04 cart payment surface (/api/payments/v2026-04/carts/{cart_token}/... paths, authenticated by the cart token in the path with no bearer — payment-method selection, VGS card tokenization, 3D Secure verification, and PayPal/Braintree/Klarna/Apple Pay flows); api-reference/commerce-v2026-04.yaml covers the v2026-04 commerce order-editing surface (/api/v202604/orders/{order_id}/edits paths, bearer-authenticated — post-checkout order edits that atomically insert items and add adjustments/discounts, with an optional dry-run preview); api-reference/webhooks-v0.yaml covers the unversioned webhooks surface (/api/... paths — webhook registration, delivery payloads, callback registrations, company events, and webhook/callback schemas).
> Successful responses wrap the resource payload alongside a top-level integer status and a meta object.

# Atomically apply one or more edits to an existing order

> Inserts items and / or adds general-purpose adjustments to an order
in a single DB transaction. After the operations run, the eight
`current_*` totals on the order are recalculated and an order-edit
audit record is written per operation.

On success, the `order_edited` lifecycle event fires so subscribed
webhooks receive the post-edit payload.

`editor_type` and `editor_id` are derived from the authenticated
credential — droplet installations land as `("droplet", handle)`,
admin user_company tokens as `("admin", user_company.id)`, other
tokens as `("system", auth_type)`. The payload itself never
carries these fields.

A typical use case is a droplet-driven free-gift flow: insert
a $X gift item plus a -$X credit adjustment in one block; the
order's `current_amount` is unchanged, but the audit log captures
both operations and webhook subscribers see the updated state.




## OpenAPI

````yaml /api-reference/commerce-v2026-04.yaml post /api/v202604/orders/{order_id}/edits
openapi: 3.1.0
info:
  title: Fluid Commerce v2026-04 API
  version: v2026-04
  description: >-
    Post-checkout order editing — atomically insert items and adjustments into
    existing orders. Powers integrations like droplet-driven free-gift flows.
  contact:
    email: support@fluid.app
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
servers:
  - url: https://api.fluid.app
  - url: https://{company}.fluid.app
    description: Production server with company subdomain
    variables:
      company:
        default: myco
        description: Company subdomain
security: []
tags:
  - name: order_edits
    description: >-
      Post-checkout order editing — atomically insert items and add adjustments
      or discounts to existing orders, with an optional dry-run preview of the
      recalculated totals.
paths:
  /api/v202604/orders/{order_id}/edits:
    post:
      tags:
        - order_edits
      summary: Atomically apply one or more edits to an existing order
      description: |
        Inserts items and / or adds general-purpose adjustments to an order
        in a single DB transaction. After the operations run, the eight
        `current_*` totals on the order are recalculated and an order-edit
        audit record is written per operation.

        On success, the `order_edited` lifecycle event fires so subscribed
        webhooks receive the post-edit payload.

        `editor_type` and `editor_id` are derived from the authenticated
        credential — droplet installations land as `("droplet", handle)`,
        admin user_company tokens as `("admin", user_company.id)`, other
        tokens as `("system", auth_type)`. The payload itself never
        carries these fields.

        A typical use case is a droplet-driven free-gift flow: insert
        a $X gift item plus a -$X credit adjustment in one block; the
        order's `current_amount` is unchanged, but the audit log captures
        both operations and webhook subscribers see the updated state.
      operationId: commerce_v2026_04_create_order_edit
      parameters:
        - $ref: '#/components/parameters/OrderId'
        - name: Idempotency-Key
          in: header
          required: false
          description: |
            Optional client-generated key (UUID recommended) that the server
            uses to deduplicate retries. The first request with a given key
            executes normally; subsequent requests with the same key return
            the cached response with `X-Idempotent-Replayed: true` for 24
            hours. A concurrent retry while the first is still in flight
            returns HTTP 409. The cache is scoped to the authenticated
            company.
          schema:
            type: string
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrderEditRequest'
            examples:
              free_gift:
                summary: Insert a free gift with offsetting credit
                value:
                  operations:
                    - type: insert_item
                      variant_id: abc123
                      quantity: 1
                      price: '29.99'
                    - type: add_adjustment
                      amount: '-29.99'
                      label: Free gift credit
                      adjustment_type: credit
                      source_type: droplet
                      source_id: free-gift-droplet
      responses:
        '200':
          description: Edit applied successfully. Returns the updated order.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderResponse'
              examples:
                edited:
                  summary: Order after applying the free-gift edit
                  value:
                    order:
                      id: 90210
                      order_number: '1001'
                      status: complete
                      currency_code: USD
                      current_subtotal: '129.99'
                      current_discount: '0.00'
                      current_adjustment_total: '-29.99'
                      current_amount: '100.00'
                      adjustments:
                        - id: 55
                          amount: '-29.99'
                          label: Free gift credit
                          adjustment_type: credit
                          source_type: droplet
                          source_id: free-gift-droplet
                          created_at: '2026-04-01T12:00:00Z'
                    status: 200
                    meta:
                      request_id: 018f2c1a-8b3a-7e6d-9f10-2a3b4c5d6e7f
                      timestamp: '2026-04-01T12:00:00Z'
          headers:
            X-Idempotent-Replayed:
              description: >-
                Present and `true` when the response is a cached replay of a
                previous request with the same Idempotency-Key.
              schema:
                type: string
                enum:
                  - 'true'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
      security:
        - bearer_auth: []
components:
  parameters:
    OrderId:
      name: order_id
      in: path
      required: true
      description: |
        Primary-key id of the order to edit. Must belong to the authenticated
        company; an unknown id or an order owned by another company returns
        404.
      schema:
        type: integer
  schemas:
    OrderEditRequest:
      type: object
      additionalProperties: false
      required:
        - operations
      properties:
        expected_updated_at:
          type: string
          format: date-time
          description: |
            Optional optimistic-concurrency guard. When provided, the
            server compares against the order's current `updated_at`
            before applying any operations and returns HTTP 409 with
            the current value on mismatch. Pass the ISO-8601 string
            received in a prior order response verbatim. Callers that
            don't load the order first can omit this and bypass the
            check.
        operations:
          type: array
          minItems: 1
          description: >-
            One or more edit operations to apply atomically. The whole
            transaction commits or rolls back together.
          items:
            oneOf:
              - $ref: '#/components/schemas/InsertItemOperation'
              - $ref: '#/components/schemas/RemoveItemOperation'
              - $ref: '#/components/schemas/ModifyItemQuantityOperation'
              - $ref: '#/components/schemas/AddAdjustmentOperation'
              - $ref: '#/components/schemas/ApplyDiscountOperation'
              - $ref: '#/components/schemas/RemoveDiscountOperation'
            discriminator:
              propertyName: type
              mapping:
                insert_item:
                  $ref: '#/components/schemas/InsertItemOperation'
                remove_item:
                  $ref: '#/components/schemas/RemoveItemOperation'
                modify_item_quantity:
                  $ref: '#/components/schemas/ModifyItemQuantityOperation'
                add_adjustment:
                  $ref: '#/components/schemas/AddAdjustmentOperation'
                apply_discount:
                  $ref: '#/components/schemas/ApplyDiscountOperation'
                remove_discount:
                  $ref: '#/components/schemas/RemoveDiscountOperation'
    OrderResponse:
      description: |
        Order response wrapper. The `order` object includes the eight
        recalculated `current_*` totals plus the `adjustments` array.
        Edit history is not exposed through this API.
      type: object
      required:
        - order
        - status
        - meta
      properties:
        order:
          $ref: '#/components/schemas/Order'
          description: >-
            The edited order, including the eight recalculated `current_*`
            totals and its `adjustments`.
        status:
          type: integer
          description: HTTP status code, repeated in the body (`200` on success).
        meta:
          $ref: '#/components/schemas/Meta'
          description: Response metadata (`request_id` and `timestamp`).
    InsertItemOperation:
      type: object
      additionalProperties: false
      required:
        - type
        - variant_id
        - quantity
        - price
      properties:
        type:
          type: string
          enum:
            - insert_item
          description: Operation discriminator; must be `insert_item` for this operation.
        variant_id:
          type: string
          description: >-
            Identifier of the product variant to insert. Must belong to the
            authenticated company.
        quantity:
          type: integer
          minimum: 1
          description: Number of units of the variant to insert. Must be at least 1.
        price:
          type: string
          format: decimal
          pattern: ^\d+(\.\d+)?$
          description: >-
            Per-unit price as a decimal string (e.g. "29.99"). String avoids
            JSON float precision loss. Must be non-negative.
        overridden:
          type: boolean
          default: false
          description: |
            Marks the operation as a manual override (e.g. a non-standard
            price). When true, the request requires the `orders.override`
            permission in addition to `orders.update`; without it the
            request returns HTTP 403 with the wrapped `ErrorResponse`
            body. The server never infers an override from the price
            value — only this explicit flag triggers the check.
    RemoveItemOperation:
      type: object
      additionalProperties: false
      required:
        - type
        - item_id
      properties:
        type:
          type: string
          enum:
            - remove_item
          description: Operation discriminator; must be `remove_item` for this operation.
        item_id:
          type: string
          description: >-
            Identifier of the order item to remove. Must belong to the order
            being edited.
    ModifyItemQuantityOperation:
      type: object
      additionalProperties: false
      required:
        - type
        - item_id
        - quantity
      properties:
        type:
          type: string
          enum:
            - modify_item_quantity
          description: >-
            Operation discriminator; must be `modify_item_quantity` for this
            operation.
        item_id:
          type: string
          description: >-
            Identifier of the order item to modify. Must belong to the order
            being edited.
        quantity:
          type: integer
          minimum: 1
          description: >-
            New per-item quantity. Setting quantity to zero is rejected; callers
            wanting to drop the item entirely should use remove_item.
        overridden:
          type: boolean
          default: false
          description: |
            Marks the operation as a manual override. When true, the
            request requires the `orders.override` permission in addition
            to `orders.update`; without it the request returns HTTP 403
            with the wrapped `ErrorResponse` body.
    AddAdjustmentOperation:
      type: object
      additionalProperties: false
      description: |
        Adding an adjustment is always treated as a manual override,
        regardless of `source_type`: requests containing this operation
        require the `orders.override` permission in addition to
        `orders.update`, and without it return HTTP 403 with the wrapped
        `ErrorResponse` body. Droplet installation tokens are exempt from
        granular permission checks, so droplet-driven flows (like the
        free-gift example) are unaffected.
      required:
        - type
        - amount
        - label
        - adjustment_type
      properties:
        type:
          type: string
          enum:
            - add_adjustment
          description: >-
            Operation discriminator; must be `add_adjustment` for this
            operation.
        amount:
          type: string
          format: decimal
          pattern: ^-?\d+(\.\d+)?$
          description: |
            Signed amount as a decimal string. Positive for charges, negative
            for credits. Zero is rejected — "0" / "0.00" return HTTP 422
            even though the pattern allows them syntactically.
        label:
          type: string
          description: Human-readable label (shown in admin UI / receipts).
        adjustment_type:
          type: string
          enum:
            - credit
            - fee
            - correction
            - promo
          description: >-
            Category of the adjustment — one of `credit`, `fee`, `correction`,
            or `promo` — used for reporting and how it is displayed on receipts.
        description:
          type:
            - string
            - 'null'
          description: Optional longer-form explanation.
        source_type:
          type:
            - string
            - 'null'
          description: Optional integration tag (e.g. "droplet").
        source_id:
          type:
            - string
            - 'null'
          description: Optional integration identifier (e.g. droplet handle).
    ApplyDiscountOperation:
      type: object
      additionalProperties: false
      description: |
        Applying a manually created discount is treated as a manual
        override and requires the `orders.override` permission in
        addition to `orders.update`; promo- and droplet-source discounts
        need only `orders.update`.
      required:
        - type
        - discount_id
      properties:
        type:
          type: string
          enum:
            - apply_discount
          description: >-
            Operation discriminator; must be `apply_discount` for this
            operation.
        discount_id:
          type: string
          description: >-
            Identifier of the discount to apply. Must belong to the
            authenticated company.
    RemoveDiscountOperation:
      type: object
      additionalProperties: false
      description: |
        Removing a manually created discount is treated as a manual
        override and requires the `orders.override` permission in
        addition to `orders.update`; promo- and droplet-source discounts
        need only `orders.update`.
      required:
        - type
        - discount_id
      properties:
        type:
          type: string
          enum:
            - remove_discount
          description: >-
            Operation discriminator; must be `remove_discount` for this
            operation.
        discount_id:
          type: string
          description: >-
            Identifier of the discount to remove. Must be currently attached to
            the order; otherwise the call returns 422.
    Order:
      type: object
      description: Order fields returned by the order-edit endpoints (subset shown).
      properties:
        id:
          type: integer
          description: >-
            The order's primary-key id — the `order_id` addressed in the edit
            paths.
        order_number:
          type: string
          description: >-
            Human-facing order number shown to buyers and staff, distinct from
            the numeric `id`.
        status:
          type: string
          description: The order's current lifecycle status (e.g. `complete`).
        currency_code:
          type: string
          description: >-
            ISO 4217 currency code the order's money amounts are expressed in
            (e.g. `USD`).
        amount:
          type: string
          description: >-
            Original checkout total (immutable). For the recalculated equivalent
            see `current_amount`.
        tax:
          type: string
          description: >-
            Original checkout tax. Preserved on the order and surfaced as
            `current_tax` for symmetry.
        shipping:
          type: string
          description: Original checkout shipping cost.
        amount_in_base:
          type: string
          description: Total in base currency (USD).
        base_to_currency_rate:
          type: string
          description: Exchange rate used to convert to the base currency.
        current_subtotal:
          type: string
          description: Recalculated subtotal after post-checkout edits.
        current_discount:
          type: string
          description: |
            Recalculated discount total after post-checkout edits: the
            absolute sum of the order's pricing-adjustment amounts, gated by
            its discounts. Subtracted in `current_amount`.
        current_adjustment_total:
          type: string
          description: Sum of post-checkout `OrderAdjustment.amount` values.
        current_tax:
          type: string
          description: Preserved from checkout (not recalculated).
        current_shipping:
          type: string
          description: Preserved from checkout.
        current_amount:
          type: string
          description: |
            current_subtotal − current_discount + current_adjustment_total
            + current_tax + current_shipping.
        current_cv:
          type: string
          format: decimal
          description: |
            Recalculated commissionable volume (CV) after post-checkout
            edits: each item's per-unit CV multiplied by its quantity, summed
            across items. A whole-number volume emitted as a decimal string;
            soft-removed items (quantity 0) contribute zero.
        current_qv:
          type: string
          format: decimal
          description: |
            Recalculated qualifying volume (QV) after post-checkout edits:
            each item's per-unit QV multiplied by its quantity, summed across
            items. A whole-number volume emitted as a decimal string;
            soft-removed items (quantity 0) contribute zero.
        adjustments:
          type: array
          description: >-
            General-purpose adjustments currently attached to the order (each a
            persisted `add_adjustment`); empty array when none.
          items:
            $ref: '#/components/schemas/Adjustment'
    Meta:
      type: object
      properties:
        request_id:
          type:
            - string
            - 'null'
          description: |
            Server-assigned identifier for this request, echoed for
            correlation with logs and support; `null` when none was assigned.
        timestamp:
          type: string
          format: date-time
          description: >-
            Server time the response was generated, as an ISO-8601 UTC
            timestamp.
    ErrorResponse:
      type: object
      required:
        - error
        - status
        - meta
      properties:
        error:
          type: object
          description: >-
            Error detail object carrying the human-readable `message` and
            optional field-level `details`.
          required:
            - message
          properties:
            message:
              type: string
              description: Human-readable summary of what went wrong.
            details:
              type: object
              additionalProperties:
                $ref: '#/components/schemas/JsonValue'
              description: Field-level error details (e.g. failed validations).
        status:
          type: integer
          description: >-
            HTTP status code of the error, repeated in the body for convenience
            (e.g. `422`).
        meta:
          $ref: '#/components/schemas/Meta'
          description: Response metadata (`request_id` and `timestamp`).
    UnauthorizedError:
      type: object
      description: |
        Bare 401 body — a single `message` string, not the wrapped
        `ErrorResponse`.
      required:
        - message
      properties:
        message:
          type: string
          description: >-
            Human-readable reason the credential was rejected (e.g. missing or
            invalid bearer token).
          example: Invalid credentials.
    ForbiddenError:
      description: |
        One of the three 403 bodies:
        - bare `{ message }` when the credential is not company-admin tier
          (e.g. a rep token)
        - bare `{ error }` when the admin-tier credential lacks the
          `orders.update` permission
        - the wrapped `ErrorResponse` when the credential has `orders.update`
          but sends an override-shaped operation without the
          `orders.override` permission
      oneOf:
        - type: object
          required:
            - message
          properties:
            message:
              type: string
              description: |
                Authorization failure message. Present on the bare-`message`
                403 body returned when the credential is not company-admin
                tier (e.g. a rep token).
              example: Not authorized.
        - type: object
          required:
            - error
          properties:
            error:
              type: string
              description: |
                Authorization failure message. Present on the bare-`error`
                403 body returned when an admin-tier credential lacks the
                `orders.update` permission.
              example: Not authorized.
        - $ref: '#/components/schemas/ErrorResponse'
    ConflictError:
      description: |
        One of the two 409 bodies:
        - the wrapped `ErrorResponse` when `expected_updated_at` mismatched
          the order's current value; `details.current_updated_at` carries
          the value to re-fetch and echo back on retry
        - a bare `{ status, message }` object when a request with the same
          `Idempotency-Key` is still in flight; `status` is the literal
          string "error", not an HTTP status code
      oneOf:
        - $ref: '#/components/schemas/ErrorResponse'
        - type: object
          required:
            - status
            - message
          properties:
            status:
              type: string
              enum:
                - error
              description: >-
                Literal string `"error"` — a status label, not an HTTP status
                code. Marks the in-flight-`Idempotency-Key` conflict body.
            message:
              type: string
              description: >-
                Explains that a request with the same `Idempotency-Key` is still
                being processed; retry once it completes.
              example: A request with this Idempotency-Key is already being processed
    Adjustment:
      type: object
      description: >-
        A general-purpose order adjustment persisted by an `add_adjustment`
        operation, as returned on the order's `adjustments` array.
      properties:
        id:
          type: integer
          description: Identifier of the persisted order adjustment.
        amount:
          type: string
          description: >-
            Signed decimal string (e.g. "-29.99"). Positive for charges,
            negative for credits.
        label:
          type: string
          description: >-
            Human-readable label supplied on the `add_adjustment` operation;
            shown in admin UI and on receipts.
        adjustment_type:
          type: string
          enum:
            - credit
            - fee
            - correction
            - promo
          description: >-
            Category of the adjustment — one of `credit`, `fee`, `correction`,
            or `promo`.
        description:
          type:
            - string
            - 'null'
          description: >-
            Longer-form explanation supplied on write; `null` when none was
            given.
        source_type:
          type:
            - string
            - 'null'
          description: Integration tag set on write (e.g. "droplet").
        source_id:
          type:
            - string
            - 'null'
          description: Integration identifier set on write (e.g. droplet handle).
        created_at:
          type: string
          format: date-time
          description: When the adjustment was created, as an ISO-8601 UTC timestamp.
    JsonValue:
      description: >-
        Any valid JSON value for provider, integration, theme, metadata, or
        other dynamic payloads whose keys are not fixed by the API contract.
      anyOf:
        - type: string
        - type: number
        - type: boolean
        - type: 'null'
        - type: array
          items:
            $ref: '#/components/schemas/JsonValue'
        - type: object
          additionalProperties:
            $ref: '#/components/schemas/JsonValue'
  responses:
    BadRequest:
      description: >-
        Malformed payload. `details` carries Dry-Validation field errors keyed
        by parameter path.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: |
        Missing or invalid bearer token. The body is a bare `{ message }`
        object — not the wrapped `ErrorResponse` envelope used by
        400 / 404 / 422.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/UnauthorizedError'
    Forbidden:
      description: |
        Authenticated but not authorized. Three paths produce 403, each
        with its own body shape:
        - Credential is not company-admin tier (e.g. a rep token) —
          bare `{ message }`.
        - Admin tier but missing the `orders.update` permission —
          bare `{ error }`.
        - Has `orders.update` but sends an override-shaped operation
          (an adjustment op, a line-item op flagged `overridden: true`,
          or a manual-source discount op) without the `orders.override`
          permission — wrapped `ErrorResponse`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ForbiddenError'
    NotFound:
      description: Order does not exist or does not belong to the authenticated company.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Conflict:
      description: |
        Two distinct concurrent-edit cases collapse to this status, each
        with its own body shape:
          - `expected_updated_at` mismatched the order's current value —
            wrapped `ErrorResponse`; `details.current_updated_at` carries
            the value the client should re-fetch and echo back on retry.
          - An `Idempotency-Key` is already in flight on another request —
            bare `{ status, message }`. The client should retry once the
            in-flight request completes; the cached response will replay.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ConflictError'
    UnprocessableEntity:
      description: >-
        Business error (e.g. variant not found, editor failure, cross-company
        discount).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    bearer_auth:
      type: http
      scheme: bearer
      description: |
        Bearer token authentication. Accepts company tokens, partner tokens,
        public tokens, droplet installation tokens, and user_company tokens.
        The authenticated credential determines `editor_type` and `editor_id`
        on the resulting order-edit audit records — the payload never carries
        either field.

````