# Add, change and delete line items in one save

```http
POST https://secure.yoprint.com/v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/order_item/bulk
```

- API version: v2
- Operation ID: `v2_public_order_item_bulk`
- Section: Sales › [Sales order line items](https://developer.yoprint.com/reference/sales-order-line-items.md)
- Web page: https://developer.yoprint.com/reference/sales-order-line-items/v2-order-item-bulk/
- Authentication: `Authorization: Bearer YOUR_API_KEY`

Runs several line operations on one sales order or quote as one save: all of them are written, or none. Sending the same batch again adds the lines again, so read the order back before you retry after a timeout.

The body is a JSON **array** of operations. Each has your own `bid`, an `action` and, depending on the action:

- `create`: `payload` is the body of Add a line item;
- `update`: `cart_item_id` (the line's id) and `payload`, the body of Update a line item (send only what changes);
- `delete`: `cart_item_id` only.

Whatever the order in the array, deletes run first, then creates, then updates. Prices, job decoration pricing, shipments, invoices and totals are recalculated once, at the end.

The optional header `X-Yoprint-Order-Item-Save-Intent` says what the save is for. The web app sends `save`.

- `save`: when nothing in the batch changes anything, nothing is written and `has_changes` is false;
- `recalculate`: always recalculates, even when only a description or position changed;
- left out (or any other value): the older behavior, where `has_changes` is always true.

Amounts and quantities are sent as `{ value, dp }` pairs: `value` is the number without its decimal point and `dp` is how many of its digits come after the decimal point. 12.50 is `{ "value": 1250, "dp": 2 }`; 24 is `{ "value": 24, "dp": 0 }`. `value` is a whole number (a JSON integer or a string of digits); `dp` is 0 to 30.

`{order_unique_key}` is the sales order or quote `id` (for example `V4keXwwxMQyXw7`) or its `unique_key` (it starts with `ord_`). The order number people see, such as SO1001, is not accepted here.

The answer, in `data`, lists every line of the order after the save, by `position`, each as `{ object_type, action_performed, has_changes, lock_version, item, bid }`. A line created by this request carries its operation's `bid`; other lines have `bid: null`. Ignore `action_performed` and `has_changes` on individual rows: `action_performed` is always `CREATED` and `has_changes` is always `true`. The `has_changes` next to `data` tells whether anything changed.

A new line is refused with HTTP 422 if the product or variant cannot be sold:

- Simple product phased out: 422 `product_phased_out` (`error.product_id` names it)
- Simple product deleted: 422 `product_deleted` (`error.product_id` names it)
- Size or color phased out: 422 `variant_phased_out` (`error.variant_id` names it)
- Size or color deleted: 422 `variant_deleted` (`error.variant_id` names it)

This holds for quotes, published orders (Live Edit), and the public API alike. Resume the product, or its size or color, in Products to sell it again. Existing lines keep working. A refused batch writes none of its lines and names the refused line in `error.bid`.

## Permission

Needs permission to edit this sales order or quote:

- The team owner
- A role with `update_all_order`
- A role with `update_self_order`, when the order was created by or is assigned to the API key's user
- The person who created a draft, with `create_order`

Quotes use the same sales order permissions.

## Business rules

- The body is an array. Each operation needs `bid` and `action` (`create`, `update` or `delete`). `create` and `update` need `payload`; `update` and `delete` need `cart_item_id`.
- Deletes run first, then creates, then updates, whatever their order in the array.
- One refused operation refuses the whole batch: nothing is saved. Most refusals name the operation in `error.bid`; a product that is not found (no envelope), a `product.type` change, a storage range error (`error.context.item_bid`) and malformed operation fields (`validation_failed`, `error.fields` like `0.bid`) do not.
- Every create and update follows the same rules as the single Add and Update operations. In particular a new catalog line needs its catalog cost in `pricing.catalog` unless it is customer-supplied; without it the whole batch is refused with `bulk_validation_payload_failed`, naming the operation's `bid` and "Enter the catalog cost for this line." on `pricing.catalog` in `error.fields`.
- `cart_item_id` must be a line of this order: otherwise "Order item not found".
- Lines an integration brought in can't be created or deleted here.

## Side effects

- Recalculates the line's price, every job's decoration pricing (quantity breaks count all lines on a job), the order totals, profit, margin and deposit.
- Updates stock tracking for the line (inventory status and stock workflow), and, on a published sales order, reserves or releases stock as quantities change.
- Updates this line on the order's invoices (new price, excess quantity removed); when the order keeps its invoice up to date automatically, rebuilds that invoice. Rebalances payments across the invoices.
- On a published sales order with QuickBooks Online connected, queues the order's QuickBooks sync.
- Refreshes the order for people who have it open in the web app. No email is sent.
- Recalculates everything once, after all operations.

## Path parameters

- `team_slug` (string, required): Your team slug, shown as Team ID next to your API key in Profile. Example `your-team`.
- `order_unique_key` (string, required): The sales order's or quote's `id` (a hash id), or its `unique_key` (`ord_…`). The order number (`scoped_id`) does not work. Example `V4keXwwxMQyXw7`.

## Headers

- `X-Yoprint-Order-Item-Save-Intent` (string, optional): `save` (what the web app sends): a batch that changes nothing writes nothing and answers `has_changes: false` (next to `data`). `recalculate`: always recalculate. Left out: the older behavior, `has_changes` always true. One of: `"save"`, `"recalculate"`. Example `save`.

## Request body

Content type `application/json`, required.

The body is an array of objects.

Each item is one of these 3 kinds, picked by `action`:

- **Add a line** (`create`): Adds a line. `payload` is the same body as Add a line item.
  - `action` (string, required): Always `"create"`.
  - `bid` (string, required): Your own id for this operation, unique in the request. The new line comes back with this `bid`, and an error names it.
  - `payload` (object, required): Pick the branch by `product.type`.
    - One of, picked by `product.type`:
      - **Catalog product** (`db_catalog`): Add a product from a supplier catalog (one size and color).
        - `product` (object, required): What the line is. `type` picks the kind of line and can't change later.
          - `type` (string, required): Always `"db_catalog"`.
          - `properties` (object, required)
            - `customer_supplied` (boolean, required): true when the customer provides the item and you only decorate it. The product price is then 0; the line still carries the decoration price. Only then may a new catalog line leave out `pricing.catalog`. true, 1, "1" and "true" count as true.
            - `id` (string, required): The catalog item: one size and color of a supplier style. It is the supplier code, a dash and the supplier's number, for example `smr-10001`.
            - `description` (string, optional): Extra text shown under the line, for example a note about the color.
        - `pricing` (object, required): How the line is priced. YoPrint determines the product price in this order: 1. Price override: if `product.override` is true, YoPrint uses `product.total`. 2. Customer-supplied: if `customer_supplied` is true (`product.properties.customer_supplied`), the product price is 0. 3. Marked-up cost: otherwise, YoPrint takes the supplier cost in `catalog` and applies the order's pricing group markup or a matching pricing rule. A new catalog line must carry `catalog` unless `customer_supplied` is true (without it, the line is refused with 422: "Enter the catalog cost for this line.").
          - `type` (string, required): Always `"catalog_product_pricing"`.
          - `catalog` (object, optional): The supplier price the line starts from. When you send `catalog`, all four fields are required.
            - `date` (string (date), required): When the price was looked up, an ISO 8601 date such as 2026-11-03 (a date-time is accepted too).
            - `provider` (string, required): The supplier code, such as `smr` or `ssa`: the code the catalog item id starts with (`smr` for `smr-10001`), and the `provider` of that item in its product's `attr_data`. YoPrint stores it as sent and doesn't check it against your suppliers.
            - `selected` (string, required): Which supplier price `total` represents: - `piece`: standard piece price - `dozen`: dozen price tier - `case`: case price tier - `account`: your account's negotiated price (`your_price` in catalog lookups) - `sales`: the supplier's sale price (`sales_price` in catalog lookups) - `custom`: a custom cost you entered manually - `legacy`: lines migrated from the old editor One of: `"piece"`, `"dozen"`, `"case"`, `"account"`, `"sales"`, `"custom"`, `"legacy"`.
            - `total` (decimal pair, required): The supplier's cost for one unit. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
          - `product` (object, optional): Set your own product price for one unit with `override: true` and `total`. It replaces the marked-up catalog cost.
            - `override` (boolean, required): true to use `total` as given; false to let YoPrint work it out.
            - `total` (decimal pair, optional): The amount for one unit, as a `{ value, dp }` pair. Up to 6 decimal places are kept. See https://developer.yoprint.com/concepts/money-and-quantities.md.
          - `service` (object, optional): The decoration price added to each unit. Leave it out, or send `override: false`, to use the job's decoration price per unit. Send `override: true` with `total` to set your own. Ignored when the line has no job (`job.type` standalone).
            - `override` (boolean, required): true to use `total` instead of the job's decoration price.
            - `total` (decimal pair, optional): The decoration price for one unit. Required when `override` is true. Up to 6 decimal places. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
        - `quantity` (object, required): How many units. `type` is always `unit`.
          - `type` (string, required): Always `"unit"`.
          - `total` (decimal pair, required): The quantity as a `{ value, dp }` pair: 24 is `{ "value": 24, "dp": 0 }`. Up to 8 decimal places. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `job` (object, required): Which job of this order the line belongs to. Decoration prices and quantity breaks count every line on a job, so put all sizes of a product on the same job.
          - One of, picked by `type`:
            - **Linked to a job** (`linked`): The line belongs to one of this order's jobs and takes its decoration price.
              - `type` (string, required): Always `"linked"`.
              - `id` (string, required): The job's `id`, from the order's jobs. It must be a job of this same order.
            - **No job** (`standalone`): The line has no job, so no decoration price.
              - `type` (string, required): Always `"standalone"`.
        - `position` (integer, required): Where the line sits on the order: lines are shown by ascending `position`. The web app spaces lines 100000 apart, which leaves room to insert between them. A value outside the allowed range is replaced by the next free position at the end.
        - `weight` (object, optional): Optional, used for shipping. Leave it out to use the product's weight.
          - One of, picked by `type`:
            - **Use the product's weight** (`use_system`): Take the weight from the product (in lb).
              - `type` (string, required): Always `"use_system"`.
            - **Set the weight** (`mass`): Your own weight for one unit.
              - `type` (string, required): Always `"mass"`.
              - `total` (decimal pair, required): The weight as a `{ value, dp }` pair whose two parts are strings: 1.5 is `{ "value": "15", "dp": "1" }`. See https://developer.yoprint.com/concepts/money-and-quantities.md.
              - `unit` (string, required): One of: `"lb"`, `"kg"`.
      - **Custom simple product** (`user_simple`): Add one of your own products that has no sizes or colors.
        - `product` (object, required): What the line is. `type` picks the kind of line and can't change later.
          - `type` (string, required): Always `"user_simple"`.
          - `properties` (object, required)
            - `customer_supplied` (boolean, required): true when the customer provides the item and you only decorate it. The product price is then 0; the line still carries the decoration price.
            - `id` (string, required): The product's `id` from Products.
            - `description` (string, optional): Extra text shown under the line, for example a note about the color.
        - `pricing` (object, required): How the line is priced. YoPrint determines the product price in this order: 1. Price override: if `product.override` is true, YoPrint uses `product.total`. 2. Customer-supplied: if `customer_supplied` is true (`product.properties.customer_supplied`), the product price is 0. 3. Product pricing: otherwise, YoPrint starts from `product_cost.total` (or the price from Products when omitted or 0) and applies any matching pricing rule.
          - `type` (string, required): Always `"product_cost_service"`.
          - `product` (object, optional): Set your own product price for one unit with `override: true` and `total`.
            - `override` (boolean, required): true to use `total` as given; false to let YoPrint work it out.
            - `total` (decimal pair, optional): The amount for one unit, as a `{ value, dp }` pair. Up to 6 decimal places are kept. See https://developer.yoprint.com/concepts/money-and-quantities.md.
          - `product_cost` (object, optional): Your cost for one unit. Optional: leave it out, or send a total of 0, to use the product's price from Products.
            - `override` (boolean, required): true when you set the cost yourself.
            - `total` (decimal pair, required): The cost for one unit. 0 means "use the product's price". `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
          - `service` (object, optional): The decoration price added to each unit. Leave it out, or send `override: false`, to use the job's decoration price per unit. Send `override: true` with `total` to set your own. Ignored when the line has no job (`job.type` standalone).
            - `override` (boolean, required): true to use `total` instead of the job's decoration price.
            - `total` (decimal pair, optional): The decoration price for one unit. Required when `override` is true. Up to 6 decimal places. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
        - `quantity` (object, required): How many units. `type` is always `unit`.
          - `type` (string, required): Always `"unit"`.
          - `total` (decimal pair, required): The quantity as a `{ value, dp }` pair: 24 is `{ "value": 24, "dp": 0 }`. Up to 8 decimal places. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `job` (object, required): Which job of this order the line belongs to. Decoration prices and quantity breaks count every line on a job, so put all sizes of a product on the same job.
          - One of, picked by `type`:
            - **Linked to a job** (`linked`): The line belongs to one of this order's jobs and takes its decoration price.
              - `type` (string, required): Always `"linked"`.
              - `id` (string, required): The job's `id`, from the order's jobs. It must be a job of this same order.
            - **No job** (`standalone`): The line has no job, so no decoration price.
              - `type` (string, required): Always `"standalone"`.
        - `position` (integer, required): Where the line sits on the order: lines are shown by ascending `position`. The web app spaces lines 100000 apart, which leaves room to insert between them. A value outside the allowed range is replaced by the next free position at the end.
        - `weight` (object, optional): Optional, used for shipping. Leave it out to use the product's weight.
          - One of, picked by `type`:
            - **Use the product's weight** (`use_system`): Take the weight from the product (in lb).
              - `type` (string, required): Always `"use_system"`.
            - **Set the weight** (`mass`): Your own weight for one unit.
              - `type` (string, required): Always `"mass"`.
              - `total` (decimal pair, required): The weight as a `{ value, dp }` pair whose two parts are strings: 1.5 is `{ "value": "15", "dp": "1" }`. See https://developer.yoprint.com/concepts/money-and-quantities.md.
              - `unit` (string, required): One of: `"lb"`, `"kg"`.
      - **Custom variant product** (`db_variant`): Add one size and color of your own product.
        - `product` (object, required): What the line is. `type` picks the kind of line and can't change later.
          - `type` (string, required): Always `"db_variant"`.
          - `properties` (object, required)
            - `customer_supplied` (boolean, required): true when the customer provides the item and you only decorate it. The product price is then 0; the line still carries the decoration price.
            - `id` (string, required): The `id` of one size and color (a variant) of your product, not the product's own id. Each size or color is its own line.
            - `description` (string, optional): Extra text shown under the line, for example a note about the color.
        - `pricing` (object, required): How the line is priced. YoPrint determines the product price in this order: 1. Price override: if `product.override` is true, YoPrint uses `product.total`. 2. Customer-supplied: if `customer_supplied` is true (`product.properties.customer_supplied`), the product price is 0. 3. Product pricing: otherwise, YoPrint starts from `product_cost.total` (or the price from Products when omitted or 0) and applies any matching pricing rule.
          - `type` (string, required): Always `"product_cost_service"`.
          - `product` (object, optional): Set your own product price for one unit with `override: true` and `total`.
            - `override` (boolean, required): true to use `total` as given; false to let YoPrint work it out.
            - `total` (decimal pair, optional): The amount for one unit, as a `{ value, dp }` pair. Up to 6 decimal places are kept. See https://developer.yoprint.com/concepts/money-and-quantities.md.
          - `product_cost` (object, optional): Your cost for one unit. Optional: leave it out, or send a total of 0, to use the product's price from Products.
            - `override` (boolean, required): true when you set the cost yourself.
            - `total` (decimal pair, required): The cost for one unit. 0 means "use the product's price". `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
          - `service` (object, optional): The decoration price added to each unit. Leave it out, or send `override: false`, to use the job's decoration price per unit. Send `override: true` with `total` to set your own. Ignored when the line has no job (`job.type` standalone).
            - `override` (boolean, required): true to use `total` instead of the job's decoration price.
            - `total` (decimal pair, optional): The decoration price for one unit. Required when `override` is true. Up to 6 decimal places. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
        - `quantity` (object, required): How many units. `type` is always `unit`.
          - `type` (string, required): Always `"unit"`.
          - `total` (decimal pair, required): The quantity as a `{ value, dp }` pair: 24 is `{ "value": 24, "dp": 0 }`. Up to 8 decimal places. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `job` (object, required): Which job of this order the line belongs to. Decoration prices and quantity breaks count every line on a job, so put all sizes of a product on the same job.
          - One of, picked by `type`:
            - **Linked to a job** (`linked`): The line belongs to one of this order's jobs and takes its decoration price.
              - `type` (string, required): Always `"linked"`.
              - `id` (string, required): The job's `id`, from the order's jobs. It must be a job of this same order.
            - **No job** (`standalone`): The line has no job, so no decoration price.
              - `type` (string, required): Always `"standalone"`.
        - `position` (integer, required): Where the line sits on the order: lines are shown by ascending `position`. The web app spaces lines 100000 apart, which leaves room to insert between them. A value outside the allowed range is replaced by the next free position at the end.
        - `weight` (object, optional): Optional, used for shipping. Leave it out to use the product's weight.
          - One of, picked by `type`:
            - **Use the product's weight** (`use_system`): Take the weight from the product (in lb).
              - `type` (string, required): Always `"use_system"`.
            - **Set the weight** (`mass`): Your own weight for one unit.
              - `type` (string, required): Always `"mass"`.
              - `total` (decimal pair, required): The weight as a `{ value, dp }` pair whose two parts are strings: 1.5 is `{ "value": "15", "dp": "1" }`. See https://developer.yoprint.com/concepts/money-and-quantities.md.
              - `unit` (string, required): One of: `"lb"`, `"kg"`.
      - **Custom line item** (`custom`): Add a one-off line with a name and a price, not linked to a product.
        - `product` (object, required): What the line is. `type` picks the kind of line and can't change later.
          - `type` (string, required): Always `"custom"`.
          - `properties` (object, required)
            - `customer_supplied` (boolean, required): true when the customer provides the item and you only decorate it. The product price is then 0; the line still carries the decoration price.
            - `name` (string, required): The line's name, as the customer sees it.
            - `description` (string, optional): Extra text shown under the line, for example a note about the color.
        - `pricing` (object, required): How the line is priced. Send the unit price in `product.total` with `override: true`.
          - `type` (string, required): Always `"custom_product"`.
          - `product` (object, required): The product price for one unit. With `override: true` (what the web app sends) the line uses `total`. With `override` false or left out, YoPrint starts from `product_cost.total` (or `total` when no cost is set) and applies any matching pricing rule.
            - `total` (decimal pair, required): The price for one unit. Up to 6 decimal places are kept. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
            - `override` (boolean, optional): true to use `total` as the price.
          - `product_cost` (object, optional): Optional: your cost for one unit, used for profit and margin.
            - `total` (decimal pair, required): Your cost for one unit. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
          - `service` (object, optional): The decoration price added to each unit. Leave it out, or send `override: false`, to use the job's decoration price per unit. Send `override: true` with `total` to set your own. Ignored when the line has no job (`job.type` standalone).
            - `override` (boolean, required): true to use `total` instead of the job's decoration price.
            - `total` (decimal pair, optional): The decoration price for one unit. Required when `override` is true. Up to 6 decimal places. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
        - `quantity` (object, required): How many units. `type` is always `unit`.
          - `type` (string, required): Always `"unit"`.
          - `total` (decimal pair, required): The quantity as a `{ value, dp }` pair: 24 is `{ "value": 24, "dp": 0 }`. Up to 8 decimal places. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `job` (object, required): Which job of this order the line belongs to. Decoration prices and quantity breaks count every line on a job, so put all sizes of a product on the same job.
          - One of, picked by `type`:
            - **Linked to a job** (`linked`): The line belongs to one of this order's jobs and takes its decoration price.
              - `type` (string, required): Always `"linked"`.
              - `id` (string, required): The job's `id`, from the order's jobs. It must be a job of this same order.
            - **No job** (`standalone`): The line has no job, so no decoration price.
              - `type` (string, required): Always `"standalone"`.
        - `position` (integer, required): Where the line sits on the order: lines are shown by ascending `position`. The web app spaces lines 100000 apart, which leaves room to insert between them. A value outside the allowed range is replaced by the next free position at the end.
        - `weight` (object, optional): Optional, used for shipping. Leave it out for 0 lb.
          - `type` (string, required): Always `"mass"`.
          - `total` (decimal pair, required): The weight as a `{ value, dp }` pair whose two parts are strings: 1.5 is `{ "value": "15", "dp": "1" }`. See https://developer.yoprint.com/concepts/money-and-quantities.md.
          - `unit` (string, required): One of: `"lb"`, `"kg"`.
      - **Fee line item** (`fee`): Add a fee, such as a rush or setup fee.
        - `product` (object, required): What the line is. `type` picks the kind of line and can't change later.
          - `type` (string, required): Always `"fee"`.
          - `properties` (object, required)
            - `name` (string, required): The fee's name, for example "Rush fee".
            - `description` (string, optional): Extra text shown under the fee, for example what it covers.
        - `pricing` (object, required): The fee amount for one unit. Fees never take a decoration price.
          - `type` (string, required): Always `"fee"`.
          - `product` (object, required)
            - `total` (decimal pair, required): The fee for one unit, 0 or more. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
        - `quantity` (object, required): How many units. `type` is always `unit`.
          - `type` (string, required): Always `"unit"`.
          - `total` (decimal pair, required): The quantity as a `{ value, dp }` pair: 24 is `{ "value": 24, "dp": 0 }`. Up to 8 decimal places. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `job` (object, required): Which job of this order the line belongs to. Decoration prices and quantity breaks count every line on a job, so put all sizes of a product on the same job.
          - One of, picked by `type`:
            - **Linked to a job** (`linked`): The line belongs to one of this order's jobs and takes its decoration price.
              - `type` (string, required): Always `"linked"`.
              - `id` (string, required): The job's `id`, from the order's jobs. It must be a job of this same order.
            - **No job** (`standalone`): The line has no job, so no decoration price.
              - `type` (string, required): Always `"standalone"`.
        - `position` (integer, required): Where the line sits on the order: lines are shown by ascending `position`. The web app spaces lines 100000 apart, which leaves room to insert between them. A value outside the allowed range is replaced by the next free position at the end.
        - `weight` (object, optional): Optional, used for shipping. Leave it out for no weight.
          - `type` (string, required): Always `"mass"`.
          - `total` (decimal pair, required): The weight as a `{ value, dp }` pair whose two parts are strings: 1.5 is `{ "value": "15", "dp": "1" }`. See https://developer.yoprint.com/concepts/money-and-quantities.md.
          - `unit` (string, required): One of: `"lb"`, `"kg"`.
- **Change a line** (`update`): Changes a line. `payload` is the same body as Update a line item: send only what changes.
  - `action` (string, required): Always `"update"`.
  - `bid` (string, required): Your own id for this operation, unique in the request. An error names it.
  - `cart_item_id` (string, required): The line's `id` (a hash id) from the order's line items. It must be a line of this order. (Its `orditm_` unique key is also accepted, but no API answer shows it.)
  - `payload` (object, required): Send only what changes. Each object you send (`product`, `pricing`, `quantity`, `job`, `weight`) must carry its `type`, and `product.type` and `pricing.type` must stay the line's own. Fields you leave out keep their value.
    - Any of, picked by `product.type`:
      - **Catalog product** (`db_catalog`): Change a product from a supplier catalog (one size and color).
        - `product` (object, optional): What the line is. `type` picks the kind of line and can't change later.
          - `type` (string, required): Always `"db_catalog"`.
          - `properties` (object, optional)
            - `description` (string, optional): Extra text shown under the line, for example a note about the color.
        - `job` (object, optional): Which job of this order the line belongs to. Decoration prices and quantity breaks count every line on a job, so put all sizes of a product on the same job.
          - One of, picked by `type`:
            - **Linked to a job** (`linked`): The line belongs to one of this order's jobs and takes its decoration price.
              - `type` (string, required): Always `"linked"`.
              - `id` (string, required): The job's `id`, from the order's jobs. It must be a job of this same order.
            - **No job** (`standalone`): The line has no job, so no decoration price.
              - `type` (string, required): Always `"standalone"`.
        - `position` (integer, optional): Where the line sits on the order: lines are shown by ascending `position`. The web app spaces lines 100000 apart, which leaves room to insert between them. A value outside the allowed range is replaced by the next free position at the end.
        - `pricing` (object, optional): How the line is priced. YoPrint determines the product price in this order: 1. Price override: if `product.override` is true, YoPrint uses `product.total`. 2. Customer-supplied: if `customer_supplied` is true (`product.properties.customer_supplied`), the product price is 0. 3. Marked-up cost: otherwise, YoPrint takes the supplier cost in `catalog` and applies the order's pricing group markup or a matching pricing rule. On an update, `catalog` is optional; send it to change the supplier cost.
          - `type` (string, required): Always `"catalog_product_pricing"`.
          - `catalog` (object, optional): The supplier price the line starts from. When you send `catalog`, all four fields are required.
            - `date` (string (date), required): When the price was looked up, an ISO 8601 date such as 2026-11-03 (a date-time is accepted too).
            - `provider` (string, required): The supplier code, such as `smr` or `ssa`: the code the catalog item id starts with (`smr` for `smr-10001`), and the `provider` of that item in its product's `attr_data`. YoPrint stores it as sent and doesn't check it against your suppliers.
            - `selected` (string, required): Which supplier price `total` represents: - `piece`: standard piece price - `dozen`: dozen price tier - `case`: case price tier - `account`: your account's negotiated price (`your_price` in catalog lookups) - `sales`: the supplier's sale price (`sales_price` in catalog lookups) - `custom`: a custom cost you entered manually - `legacy`: lines migrated from the old editor One of: `"piece"`, `"dozen"`, `"case"`, `"account"`, `"sales"`, `"custom"`, `"legacy"`.
            - `total` (decimal pair, required): The supplier's cost for one unit. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
          - `product` (object, optional): Set your own product price for one unit with `override: true` and `total`. It replaces the marked-up catalog cost.
            - `override` (boolean, required): true to use `total` as given; false to let YoPrint work it out.
            - `total` (decimal pair, optional): The amount for one unit, as a `{ value, dp }` pair. Up to 6 decimal places are kept. See https://developer.yoprint.com/concepts/money-and-quantities.md.
          - `service` (object, optional): The decoration price added to each unit. Leave it out, or send `override: false`, to use the job's decoration price per unit. Send `override: true` with `total` to set your own. Ignored when the line has no job (`job.type` standalone).
            - `override` (boolean, required): true to use `total` instead of the job's decoration price.
            - `total` (decimal pair, optional): The decoration price for one unit. Required when `override` is true. Up to 6 decimal places. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
        - `quantity` (object, optional): How many units. `type` is always `unit`.
          - `type` (string, required): Always `"unit"`.
          - `total` (decimal pair, optional): The quantity as a `{ value, dp }` pair: 24 is `{ "value": 24, "dp": 0 }`. Up to 8 decimal places. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `weight` (object, optional): Optional, used for shipping. Leave it out to use the product's weight.
          - One of, picked by `type`:
            - **Use the product's weight** (`use_system`): Take the weight from the product (in lb).
              - `type` (string, required): Always `"use_system"`.
            - **Set the weight** (`mass`): Your own weight for one unit. `unit` is required; leave `total` out to keep the current weight.
              - `type` (string, required): Always `"mass"`.
              - `unit` (string, required): One of: `"lb"`, `"kg"`.
              - `total` (decimal pair, optional): The weight as a `{ value, dp }` pair whose two parts are strings: 1.5 is `{ "value": "15", "dp": "1" }`. See https://developer.yoprint.com/concepts/money-and-quantities.md.
      - **Custom simple product** (`user_simple`): Change one of your own products that has no sizes or colors.
        - `product` (object, optional): What the line is. `type` picks the kind of line and can't change later.
          - `type` (string, required): Always `"user_simple"`.
          - `properties` (object, optional)
            - `description` (string, optional): Extra text shown under the line, for example a note about the color.
        - `job` (object, optional): Which job of this order the line belongs to. Decoration prices and quantity breaks count every line on a job, so put all sizes of a product on the same job.
          - One of, picked by `type`:
            - **Linked to a job** (`linked`): The line belongs to one of this order's jobs and takes its decoration price.
              - `type` (string, required): Always `"linked"`.
              - `id` (string, required): The job's `id`, from the order's jobs. It must be a job of this same order.
            - **No job** (`standalone`): The line has no job, so no decoration price.
              - `type` (string, required): Always `"standalone"`.
        - `position` (integer, optional): Where the line sits on the order: lines are shown by ascending `position`. The web app spaces lines 100000 apart, which leaves room to insert between them. A value outside the allowed range is replaced by the next free position at the end.
        - `pricing` (object, optional): How the line is priced. YoPrint determines the product price in this order: 1. Price override: if `product.override` is true, YoPrint uses `product.total`. 2. Customer-supplied: if `customer_supplied` is true (`product.properties.customer_supplied`), the product price is 0. 3. Product pricing: otherwise, YoPrint starts from `product_cost.total` (or the price from Products when omitted or 0) and applies any matching pricing rule.
          - `type` (string, required): Always `"product_cost_service"`.
          - `product` (object, optional): Set your own product price for one unit with `override: true` and `total`.
            - `override` (boolean, required): true to use `total` as given; false to let YoPrint work it out.
            - `total` (decimal pair, optional): The amount for one unit, as a `{ value, dp }` pair. Up to 6 decimal places are kept. See https://developer.yoprint.com/concepts/money-and-quantities.md.
          - `product_cost` (object, optional): Your cost for one unit. Optional: leave it out, or send a total of 0, to use the product's price from Products.
            - `override` (boolean, required): true when you set the cost yourself.
            - `total` (decimal pair, required): The cost for one unit. 0 means "use the product's price". `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
          - `service` (object, optional): The decoration price added to each unit. Leave it out, or send `override: false`, to use the job's decoration price per unit. Send `override: true` with `total` to set your own. Ignored when the line has no job (`job.type` standalone).
            - `override` (boolean, required): true to use `total` instead of the job's decoration price.
            - `total` (decimal pair, optional): The decoration price for one unit. Required when `override` is true. Up to 6 decimal places. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
        - `quantity` (object, optional): How many units. `type` is always `unit`.
          - `type` (string, required): Always `"unit"`.
          - `total` (decimal pair, optional): The quantity as a `{ value, dp }` pair: 24 is `{ "value": 24, "dp": 0 }`. Up to 8 decimal places. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `weight` (object, optional): Optional, used for shipping. Leave it out to use the product's weight.
          - One of, picked by `type`:
            - **Use the product's weight** (`use_system`): Take the weight from the product (in lb).
              - `type` (string, required): Always `"use_system"`.
            - **Set the weight** (`mass`): Your own weight for one unit. `unit` is required; leave `total` out to keep the current weight.
              - `type` (string, required): Always `"mass"`.
              - `unit` (string, required): One of: `"lb"`, `"kg"`.
              - `total` (decimal pair, optional): The weight as a `{ value, dp }` pair whose two parts are strings: 1.5 is `{ "value": "15", "dp": "1" }`. See https://developer.yoprint.com/concepts/money-and-quantities.md.
      - **Custom variant product** (`db_variant`): Change one size and color of your own product.
        - `product` (object, optional): What the line is. `type` picks the kind of line and can't change later.
          - `type` (string, required): Always `"db_variant"`.
          - `properties` (object, optional)
            - `description` (string, optional): Extra text shown under the line, for example a note about the color.
        - `job` (object, optional): Which job of this order the line belongs to. Decoration prices and quantity breaks count every line on a job, so put all sizes of a product on the same job.
          - One of, picked by `type`:
            - **Linked to a job** (`linked`): The line belongs to one of this order's jobs and takes its decoration price.
              - `type` (string, required): Always `"linked"`.
              - `id` (string, required): The job's `id`, from the order's jobs. It must be a job of this same order.
            - **No job** (`standalone`): The line has no job, so no decoration price.
              - `type` (string, required): Always `"standalone"`.
        - `position` (integer, optional): Where the line sits on the order: lines are shown by ascending `position`. The web app spaces lines 100000 apart, which leaves room to insert between them. A value outside the allowed range is replaced by the next free position at the end.
        - `pricing` (object, optional): How the line is priced. YoPrint determines the product price in this order: 1. Price override: if `product.override` is true, YoPrint uses `product.total`. 2. Customer-supplied: if `customer_supplied` is true (`product.properties.customer_supplied`), the product price is 0. 3. Product pricing: otherwise, YoPrint starts from `product_cost.total` (or the price from Products when omitted or 0) and applies any matching pricing rule.
          - `type` (string, required): Always `"product_cost_service"`.
          - `product` (object, optional): Set your own product price for one unit with `override: true` and `total`.
            - `override` (boolean, required): true to use `total` as given; false to let YoPrint work it out.
            - `total` (decimal pair, optional): The amount for one unit, as a `{ value, dp }` pair. Up to 6 decimal places are kept. See https://developer.yoprint.com/concepts/money-and-quantities.md.
          - `product_cost` (object, optional): Your cost for one unit. Optional: leave it out, or send a total of 0, to use the product's price from Products.
            - `override` (boolean, required): true when you set the cost yourself.
            - `total` (decimal pair, required): The cost for one unit. 0 means "use the product's price". `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
          - `service` (object, optional): The decoration price added to each unit. Leave it out, or send `override: false`, to use the job's decoration price per unit. Send `override: true` with `total` to set your own. Ignored when the line has no job (`job.type` standalone).
            - `override` (boolean, required): true to use `total` instead of the job's decoration price.
            - `total` (decimal pair, optional): The decoration price for one unit. Required when `override` is true. Up to 6 decimal places. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
        - `quantity` (object, optional): How many units. `type` is always `unit`.
          - `type` (string, required): Always `"unit"`.
          - `total` (decimal pair, optional): The quantity as a `{ value, dp }` pair: 24 is `{ "value": 24, "dp": 0 }`. Up to 8 decimal places. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `weight` (object, optional): Optional, used for shipping. Leave it out to use the product's weight.
          - One of, picked by `type`:
            - **Use the product's weight** (`use_system`): Take the weight from the product (in lb).
              - `type` (string, required): Always `"use_system"`.
            - **Set the weight** (`mass`): Your own weight for one unit. `unit` is required; leave `total` out to keep the current weight.
              - `type` (string, required): Always `"mass"`.
              - `unit` (string, required): One of: `"lb"`, `"kg"`.
              - `total` (decimal pair, optional): The weight as a `{ value, dp }` pair whose two parts are strings: 1.5 is `{ "value": "15", "dp": "1" }`. See https://developer.yoprint.com/concepts/money-and-quantities.md.
      - **Custom line item** (`custom`): Change a one-off line with a name and a price, not linked to a product.
        - `product` (object, optional): What the line is. `type` picks the kind of line and can't change later.
          - `type` (string, required): Always `"custom"`.
          - `properties` (object, optional)
            - `description` (string, optional): Extra text shown under the line, for example a note about the color.
            - `name` (string, optional): The line's name, as the customer sees it.
        - `job` (object, optional): Which job of this order the line belongs to. Decoration prices and quantity breaks count every line on a job, so put all sizes of a product on the same job.
          - One of, picked by `type`:
            - **Linked to a job** (`linked`): The line belongs to one of this order's jobs and takes its decoration price.
              - `type` (string, required): Always `"linked"`.
              - `id` (string, required): The job's `id`, from the order's jobs. It must be a job of this same order.
            - **No job** (`standalone`): The line has no job, so no decoration price.
              - `type` (string, required): Always `"standalone"`.
        - `position` (integer, optional): Where the line sits on the order: lines are shown by ascending `position`. The web app spaces lines 100000 apart, which leaves room to insert between them. A value outside the allowed range is replaced by the next free position at the end.
        - `pricing` (object, optional): How the line is priced. Send the unit price in `product.total` with `override: true`.
          - `type` (string, required): Always `"custom_product"`.
          - `product` (object, optional): The product price for one unit. With `override: true` (what the web app sends) the line uses `total`. With `override` false or left out, YoPrint starts from `product_cost.total` (or `total` when no cost is set) and applies any matching pricing rule.
            - `total` (decimal pair, required): The price for one unit. Up to 6 decimal places are kept. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
            - `override` (boolean, optional): true to use `total` as the price.
          - `product_cost` (object, optional): Optional: your cost for one unit, used for profit and margin.
            - `total` (decimal pair, required): Your cost for one unit. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
          - `service` (object, optional): The decoration price added to each unit. Leave it out, or send `override: false`, to use the job's decoration price per unit. Send `override: true` with `total` to set your own. Ignored when the line has no job (`job.type` standalone).
            - `override` (boolean, required): true to use `total` instead of the job's decoration price.
            - `total` (decimal pair, optional): The decoration price for one unit. Required when `override` is true. Up to 6 decimal places. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
        - `quantity` (object, optional): How many units. `type` is always `unit`.
          - `type` (string, required): Always `"unit"`.
          - `total` (decimal pair, optional): The quantity as a `{ value, dp }` pair: 24 is `{ "value": 24, "dp": 0 }`. Up to 8 decimal places. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `weight` (object, optional): Optional, used for shipping.
          - `type` (string, required): Always `"mass"`.
          - `unit` (string, required): One of: `"lb"`, `"kg"`.
          - `total` (decimal pair, optional): The weight as a `{ value, dp }` pair whose two parts are strings: 1.5 is `{ "value": "15", "dp": "1" }`. See https://developer.yoprint.com/concepts/money-and-quantities.md.
      - **Fee line item** (`fee`): Change a fee, such as a rush or setup fee.
        - `product` (object, optional): What the line is. `type` picks the kind of line and can't change later.
          - `type` (string, required): Always `"fee"`.
          - `properties` (object, optional)
            - `description` (string, optional): Extra text shown under the fee, for example what it covers.
            - `name` (string, optional): The fee's name, for example "Rush fee".
        - `job` (object, optional): Which job of this order the line belongs to. Decoration prices and quantity breaks count every line on a job, so put all sizes of a product on the same job.
          - One of, picked by `type`:
            - **Linked to a job** (`linked`): The line belongs to one of this order's jobs and takes its decoration price.
              - `type` (string, required): Always `"linked"`.
              - `id` (string, required): The job's `id`, from the order's jobs. It must be a job of this same order.
            - **No job** (`standalone`): The line has no job, so no decoration price.
              - `type` (string, required): Always `"standalone"`.
        - `position` (integer, optional): Where the line sits on the order: lines are shown by ascending `position`. The web app spaces lines 100000 apart, which leaves room to insert between them. A value outside the allowed range is replaced by the next free position at the end.
        - `pricing` (object, optional): The fee amount for one unit. Fees never take a decoration price.
          - `type` (string, required): Always `"fee"`.
          - `product` (object, optional)
            - `total` (decimal pair, required): The fee for one unit, 0 or more. `{ "value": 1250, "dp": 2 }` is 12.50 (see https://developer.yoprint.com/concepts/money-and-quantities.md).
        - `quantity` (object, optional): How many units. `type` is always `unit`.
          - `type` (string, required): Always `"unit"`.
          - `total` (decimal pair, optional): The quantity as a `{ value, dp }` pair: 24 is `{ "value": 24, "dp": 0 }`. Up to 8 decimal places. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `weight` (object, optional): Optional, used for shipping. Leave it out for no weight.
          - `type` (string, required): Always `"mass"`.
          - `unit` (string, required): One of: `"lb"`, `"kg"`.
          - `total` (decimal pair, optional): The weight as a `{ value, dp }` pair whose two parts are strings: 1.5 is `{ "value": "15", "dp": "1" }`. See https://developer.yoprint.com/concepts/money-and-quantities.md.
- **Delete a line** (`delete`): Deletes a line. A line that is already deleted is skipped without an error.
  - `action` (string, required): Always `"delete"`.
  - `bid` (string, required): Your own id for this operation, unique in the request. An error names it.
  - `cart_item_id` (string, required): The line's `id` (a hash id) from the order's line items. It must be a line of this order. (Its `orditm_` unique key is also accepted, but no API answer shows it.)

### Example request

```bash
curl -X POST 'https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/order_item/bulk' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '[
    {
      "action": "update",
      "bid": "u1",
      "cart_item_id": "Owr88lhAk28aqL",
      "payload": {
        "quantity": {
          "total": {
            "dp": 0,
            "value": 30
          },
          "type": "unit"
        }
      }
    },
    {
      "action": "delete",
      "bid": "d1",
      "cart_item_id": "HF0BYICEJ09rG2"
    }
  ]'
```

Another example: Add two sizes of a variant product to one job:

```bash
curl -X POST 'https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/order_item/bulk' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '[
    {
      "action": "create",
      "bid": "b1",
      "payload": {
        "job": {
          "id": "K6iF8n29iwOZnj",
          "type": "linked"
        },
        "position": 100000,
        "pricing": {
          "product": {
            "override": false
          },
          "service": {
            "override": false
          },
          "type": "product_cost_service"
        },
        "product": {
          "properties": {
            "customer_supplied": false,
            "id": "TGbMAohWcL31V0"
          },
          "type": "db_variant"
        },
        "quantity": {
          "total": {
            "dp": 0,
            "value": 12
          },
          "type": "unit"
        }
      }
    },
    {
      "action": "create",
      "bid": "b2",
      "payload": {
        "job": {
          "id": "K6iF8n29iwOZnj",
          "type": "linked"
        },
        "position": 200000,
        "pricing": {
          "product": {
            "override": false
          },
          "service": {
            "override": false
          },
          "type": "product_cost_service"
        },
        "product": {
          "properties": {
            "customer_supplied": false,
            "id": "ZrdJ9i78QVmdhr"
          },
          "type": "db_variant"
        },
        "quantity": {
          "total": {
            "dp": 0,
            "value": 18
          },
          "type": "unit"
        }
      }
    }
  ]'
```

## Response

`200` Successful response.

Fields:

- `data` (array of objects, required): Every line of the order after the save.
  - Each item:
    - `object_type` (string, optional): Always `"order_item"`.
    - `action_performed` (string, optional): Ignore this field here: always `CREATED` in this answer.
    - `bid` (string | null, optional): Your `bid` for a line this request created; null for other lines.
    - `has_changes` (boolean, optional): Ignore this field here: always `true` on every row. Read the top-level `has_changes` next to `data`.
    - `item` (object, optional): One line. It has the request's groups (`product`, `pricing`, `quantity`, `job`, `weight`, `position`) as saved, with the prices YoPrint worked out, and the same values again in `_meta`. A few groups differ from the request: the catalog cost comes back as `pricing.catalog.cost` with its date as a date-time, and a linked `job` also has its `unique_key` and `scoped_id` (`JOB-1001`). Each group's `_meta.editable_fields_on_update` lists the request paths an update accepts for that group (`pricing.type` is listed, but it must stay the line's own).
      - `_meta` (object, optional): The line's own values again (`id`, `type`, `order_id`, `unit_price`, `subtotal` and so on). Always an object.
      - `id` (string, optional)
      - `inventory_status` (string | null, optional): Stock status of the line.
      - `job` (object, optional)
      - `lock_version` (integer | null, optional): The line's own lock version (a microsecond timestamp).
      - `order_id` (string, optional)
      - `order_unique_key` (string, optional): The order's `unique_key`. The line's own `unique_key` is not in the answer: use `id`.
      - `position` (integer, optional)
      - `pricing` (object, optional)
      - `pricing_rule_applied` (object | null, optional): The pricing rule that set the line's product price, or null when no rule did. List the line items, Get one line item and the bulk save answer with the full summary below. Other answers carry only `type`, the two ids and the two totals.
        - `after` (string, optional): Full summary only: the price after the rule, as a decimal string.
        - `after_total` (decimal pair, optional): The product price per unit after the rule. 6 decimal places. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 1250, "dp": 2 }` is 12.50). Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `base` (string, optional): Full summary only: that starting price, as a decimal string with at least 2 decimal places.
        - `base_type` (string, optional): Full summary only: what the rule started from: the supplier price for catalog lines, the product's own price for other lines. One of: `"supplier_price"`, `"base_price"`.
        - `normal_price` (string | null, optional): Full summary only: what the line would cost with no rule. For catalog lines the supplier price plus the group's catalog markup as it is now; otherwise the same as `base`.
        - `original_total` (decimal pair, optional): The product price per unit before the rule. 6 decimal places. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 1250, "dp": 2 }` is 12.50). Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
        - `pricing_group_id` (string | null, optional): The pricing group whose rule applied.
        - `pricing_group_name` (string | null, optional): Full summary only: the group's name now.
        - `pricing_group_removed` (boolean | null, optional): Full summary only: true when the group was removed since. The line keeps its price.
        - `pricing_rule_id` (string | null, optional): The rule that applied.
        - `rule` (object | null, optional): Full summary only: the rule as the group holds it now, with its current name and position (1 is the first rule checked). Null when the rule no longer exists.
          - `id` (string, optional)
          - `label` (string | null, optional)
          - `position` (integer, optional)
        - `type` (string, optional): `product` on a line.
      - `product` (object, optional)
      - `quantity` (object, optional): The quantity, trailing zeros dropped: 10 is `{ "value": 10, "dp": 0 }`.
      - `subtotal` (decimal pair, optional): `unit_price` times the quantity. 4 decimal places: 125.00 is `{ "value": 1250000, "dp": 4 }`. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 1250, "dp": 2 }` is 12.50). Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
      - `trashed` (boolean, optional): true when the line was deleted.
      - `type` (string, optional): The kind of line: db_catalog, user_simple, db_variant, custom or fee.
      - `unit_price` (decimal pair, optional): The price of one unit: the product price plus the decoration price. 6 decimal places: 12.50 is `{ "value": 12500000, "dp": 6 }`. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 1250, "dp": 2 }` is 12.50). Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
    - `lock_version` (object, optional): The order's lock versions after this call. Each is a whole number (a microsecond timestamp) that changes on every save, or null.
      - `order` (object, required)
        - `invoice_lock_version` (integer | null, optional)
        - `job_lock_version` (integer | null, optional)
        - `order_item_lock_version` (integer | null, optional)
        - `order_lock_version` (integer | null, optional): The order's own `lock_version`.
        - `payment_lock_version` (integer | null, optional)
        - `shipment_lock_version` (integer | null, optional)
- `has_changes` (boolean, required): false when the batch changed nothing (with the `save` intent).
- `status` (integer, required)
- `success` (boolean, required)

Example response:

```json
{
  "data": [
    {
      "action_performed": "CREATED",
      "bid": "b1",
      "has_changes": true,
      "item": {
        "id": "Aj4yrI88nPeeJR",
        "job": {
          "id": "K6iF8n29iwOZnj",
          "scoped_id": "JOB-1001",
          "type": "linked",
          "unique_key": "job_EXAMPLEuniqueKey"
        },
        "lock_version": 1793696400234567,
        "order_id": "V4keXwwxMQyXw7",
        "order_unique_key": "ord_EXAMPLEuniqueKey",
        "position": 100000,
        "pricing": {
          "product": {
            "override": false,
            "total": {
              "dp": 6,
              "value": 12500000
            }
          },
          "type": "product_cost_service"
        },
        "product": {
          "properties": {
            "customer_supplied": false,
            "id": "TGbMAohWcL31V0"
          },
          "type": "db_variant"
        },
        "quantity": {
          "total": {
            "dp": 0,
            "value": 12
          },
          "type": "unit"
        },
        "subtotal": {
          "dp": 4,
          "value": 1500000
        },
        "trashed": false,
        "type": "db_variant",
        "unit_price": {
          "dp": 6,
          "value": 12500000
        }
      },
      "lock_version": {
        "order": {
          "invoice_lock_version": 1793696400123456,
          "job_lock_version": 1793696400123456,
          "order_item_lock_version": 1793696400234567,
          "order_lock_version": 1793696400234567,
          "payment_lock_version": 1793696400123456,
          "shipment_lock_version": null
        }
      },
      "object_type": "order_item"
    },
    {
      "action_performed": "CREATED",
      "bid": "b2",
      "has_changes": true,
      "item": {
        "id": "an9jhUMlXwOTLr",
        "job": {
          "id": "K6iF8n29iwOZnj",
          "scoped_id": "JOB-1001",
          "type": "linked",
          "unique_key": "job_EXAMPLEuniqueKey"
        },
        "lock_version": 1793696400234567,
        "order_id": "V4keXwwxMQyXw7",
        "order_unique_key": "ord_EXAMPLEuniqueKey",
        "position": 200000,
        "pricing": {
          "product": {
            "override": false,
            "total": {
              "dp": 6,
              "value": 12500000
            }
          },
          "type": "product_cost_service"
        },
        "product": {
          "properties": {
            "customer_supplied": false,
            "id": "ZrdJ9i78QVmdhr"
          },
          "type": "db_variant"
        },
        "quantity": {
          "total": {
            "dp": 0,
            "value": 18
          },
          "type": "unit"
        },
        "subtotal": {
          "dp": 4,
          "value": 2250000
        },
        "trashed": false,
        "type": "db_variant",
        "unit_price": {
          "dp": 6,
          "value": 12500000
        }
      },
      "lock_version": {
        "order": {
          "invoice_lock_version": 1793696400123456,
          "job_lock_version": 1793696400123456,
          "order_item_lock_version": 1793696400234567,
          "order_lock_version": 1793696400234567,
          "payment_lock_version": 1793696400123456,
          "shipment_lock_version": null
        }
      },
      "object_type": "order_item"
    }
  ],
  "has_changes": true,
  "status": 200,
  "success": true
}
```

## Errors

- `401` The API key is missing or wrong (`unauthenticated`).
- `403` You are not allowed to do this. `api_access_not_in_plan`: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only `{"message": "Insufficient Permission"}`. Other refusals use the error envelope, with `unauthorized` or `user_is_not_authorized`.
  - When: The API key's role can't edit this order (`user_is_not_authorized`). Fix: Ask the team owner for sales order edit permission, or assign the user to the order.
- `404` Not found. `page_not_found`: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's. `code: null`, with a message that starts "No query results for": there is no record with that id.
  - When: The order or line does not exist in this team: `error.code` is null and `error.message` says "No query results for …". Fix: Check the id.
- `422` The request was refused and changed nothing. `validation_failed` lists what to fix in `error.fields`; `bulk_validation_payload_failed` names the failing item's `bid` and its `fields`.
  - When: One operation is invalid (`bulk_validation_payload_failed`, with `error.bid` and `error.fields`). Fix: Fix the operation named by `bid` and resend the whole batch.
  - When: `cart_item_id` is not a line of this order: `validation_error` "Order item not found". Fix: Use line ids from this order.
  - When: A required field is missing, a `type` value is unknown, or a `{ value, dp }` pair is malformed (for example sending `{"value": 12.5, "dp": 1}` instead of `{"value": 125, "dp": 1}`: `value` must be an integer). Fix: Read `error.fields`; send whole numbers in `value` and the decimal places in `dp` (for example `{"value": 1250, "dp": 2}` for 12.50).
  - When: The product, variant or catalog item id is not found. This error has no envelope: the body is `{"message": "user_simple: Simple Product not found:48213"}`, where the number is YoPrint's internal number for the id you sent (`db_variant: Variant Product not found:…` the same way; `db_catalog: Catalog Product not found:smr-10001` repeats the catalog id). Fix: Use an id from Products, or a catalog item id.
  - When: `job.id` is not a job of this order: "This job is no longer available. Choose a job from this order and save again." Fix: Use a job id from this order's jobs.
  - When: The job's decoration setup needs repair: "Review this job’s services before changing quantities or adding, removing or moving items." (`validation_error`, with `error.fields.job.id`, and `error.context.reason` `job_service_repair_required` with `error.context.job_id` and `job_label`). Fix: Fix the job's services in the web app, then retry.
  - When: The product, or its size or color, is phased out or deleted (`product_phased_out`, `product_deleted`, `variant_phased_out`, `variant_deleted`). Fix: Resume it in Products, or choose another product.
  - When: A price, quantity or calculated total is out of range (`amount_out_of_range`). Fix: Check the amount and its decimal places.

## Notes

- Use this to add a product's whole size run in one save, so quantity breaks are worked out once.
- Resending the same batch adds the lines again: creates are not deduplicated by `bid`.
- Deleting a line that is already deleted counts as no change.

## Related

- [Add a line item to a sales order or quote](https://developer.yoprint.com/reference/sales-order-line-items/v2-order-item-store.md): `POST /v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/order_item`
- [Update a line item](https://developer.yoprint.com/reference/sales-order-line-items/v2-order-item-update.md): `PUT /v2/api/store/{team_slug}/v2/sales_order/order_item/{order_item_unique_key}`
- [Delete a line item](https://developer.yoprint.com/reference/sales-order-line-items/v2-order-item-destroy.md): `DELETE /v2/api/store/{team_slug}/v2/sales_order/order_item/{order_item_unique_key}`
- [List the line items of a sales order or quote](https://developer.yoprint.com/reference/sales-order-line-items/v2-order-item-index.md): `GET /v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/order_item`
