---
title: "Adds lines to an existing sales order (the app's item picker on the Details page)."
method: POST
path: "/api/v1/sales-orders/{id}/lines"
tags: ["SalesOrders"]
---

# Adds lines to an existing sales order (the app's item picker on the Details page).

`POST /api/v1/sales-orders/{id}/lines`

## Request

- Base URL: `https://app.flexonthejob.com`
- URL: `https://app.flexonthejob.com/api/v1/sales-orders/{id}/lines`
- Auth: HTTP bearer

Requires `ManageSalesOrders` and an `Idempotency-Key` header. Body:
`{ "lines": [ { "itemId": 5, "quantity": 2, "unitPrice": 125.00 } ] }`. Every line is
checked as on create (a non-deleted catalog item, quantity 1–1,000,000, price and cost
0–1,000,000,000) before anything is written; then the lines are added exactly as in the
app: a line for an item already on the order and not in a live transfer is merged into
it (quantities add up, a non-zero price or cost replaces the old one), totals and draft
invoices are recomputed and the history records "Added n item(s) (via API key '…')".
Adding lines reserves no stock; `/fulfill` (or `/transfers`) covers them.

## Path parameters

- `id` integer, required

## Request body

- AddSalesOrderLinesApiRequest: Body of `POST /api/v1/sales-orders/{id}/lines`. Example: `{ "lines": [ { "itemId": 5, "quantity": 2, "unitPrice": 125.00 } ] }`.
  - `lines` CreateSalesOrderLineApiRequest[], required: Lines to add, 1–200 entries, each as on create; checked before anything is written. A line for an item already on the order (not in a live transfer) is merged into it, as the app does.
    - `itemId` integer: Catalog item id from `/api/v1/items`; must be a non-deleted item of this organization. Example: `5`.
    - `quantity` integer: Quantity ordered, whole units, 1–1,000,000. Example: `10`.
    - `unitPrice` number, double: Unit price in the organization's currency, 0–1,000,000,000; the line total is `quantity` × `unitPrice`, and the lines together may total at most 1,000,000,000,000 (else 422 on `lines`). Example: `125.00`.
    - `unitCost` number, double, nullable: Unit cost in the organization's currency, 0–1,000,000,000; omit when unknown (stored as 0). Example: `80.00`.
    - `notes` string, nullable: Line notes. Example: `Cut to 25 ft lengths`.

## Response `200`

Added; body is the order with its lines.

- SalesOrderDetailDto: One sales order with its lines, as returned by `GET /api/v1/sales-orders/{id}` and `POST /api/v1/sales-orders`.
  - `id` integer: Numeric id used in `/api/v1/sales-orders/{id}`. Unique within this organization. Example: `318`.
  - `syncId` string, uuid: Stable cross-device identifier (GUID) — also the pagination cursor value. Example: `7b1e2c3d-4f5a-4b6c-9d8e-0f1a2b3c4d5e`.
  - `orderNumber` string: Order number assigned by the app, `SO` + two-digit year + two-digit month + a four-digit sequence per month. Example: `SO26090007`.
  - `status` string: Order status. One of `Pending` (created, lines can be added, fulfilment not started or not covering every line), `Picking` (a pick transfer covers every line and items are being picked), `Packed` (everything picked), `Shipped` (every transfer has shipping info), `Delivered` (every transfer completed), `Complete` (terminal: marked complete, or delivered and fully invoiced and paid) and `Cancelled` (terminal). Legacy rows may still say the deprecated `Confirmed`. Over the API `/fulfill` starts fulfilment and the later statuses follow from `/pick`, `/complete-pick`, `/ship` and `/deliver`, as in the app.
  - `paymentStatus` string, nullable: Payment status: `Pending`, `Partial`, `Paid`, `Overdue`, `Refunded`, `Invoiced` or `Cancelled`. Example: `Pending`. Null on some legacy rows.
  - `fulfillmentStatus` string, nullable: Aggregate status of the order's transfers (the earliest one wins): `None` (no transfer yet), `Draft`, `Pending`, `InTransit`, `AtWaypoint`, `InTransitToWaypoint`, `InTransitToDestination`, `Completed`, `Cancelled`, `Failed`. Example: `Pending`.
  - `customerId` integer: Customer id (see `/api/v1/customers/{id}`). Example: `42`.
  - `customerName` string, nullable: Customer name at read time. Example: `Acme Plumbing`.
  - `customerPONumber` string, nullable: The customer's own purchase-order reference; null when none. Example: `PO-7781`.
  - `orderDate` string, date-time: Order date as entered in the app: the organization's local calendar date with no UTC offset (not an instant; do not convert it between time zones). Example: `2026-09-23T00:00:00`.
  - `requestedDeliveryDate` string, date-time, nullable: Requested delivery date, same organization-local calendar-date format as `orderDate`; null when none. Example: `2026-09-30T00:00:00`.
  - `subTotal` number, double: Sum of the line totals, in the organization's currency. Example: `1250.00`.
  - `taxRate` number, double: Tax rate in percent (8.25 = 8.25 %). 0 for a tax-exempt customer. Example: `8.25`.
  - `taxAmount` number, double: Tax, `subTotal` × `taxRate` / 100, in the organization's currency. Example: `103.13`.
  - `shippingCost` number, double: Shipping charged on the order, in the organization's currency. Example: `25.00`.
  - `totalAmount` number, double: Order total, `subTotal` + `taxAmount` + `shippingCost`, in the organization's currency. Example: `1378.13`.
  - `amountPaid` number, double: Amount paid so far, in the organization's currency. Example: `500.00`.
  - `balanceDue` number, double: Still owed: `totalAmount` − `amountPaid` − store credit applied, in the organization's currency. Example: `878.13`.
  - `lineCount` integer: Number of lines on the order (removed lines excluded). Example: `3`.
  - `createdAt` string, date-time: Creation time, ISO-8601 UTC. Example: `2026-09-23T14:05:00Z`.
  - `updatedAt` string, date-time, nullable: Last recorded modification (header or line edits, lines added or removed, status change, fulfilment and pick progress, `fulfillmentStatus` change), ISO-8601 UTC; null when never modified since creation. Payments do not move it (`paymentStatus` / `amountPaid` / `balanceDue` can change without it). Example: `2026-09-23T15:12:00Z`.
  - `deliveryAddressId` integer, nullable: Id of the customer address the order is delivered to; null when none. Example: `77`.
  - `paymentTerms` string, nullable: Payment terms text. Example: `Net 30`. Null when none.
  - `notes` string, nullable: Free-text notes on the order. Example: `Leave at loading dock.` Null when none.
  - `lines` SalesOrderLineDto[]: The order lines (removed lines excluded), in the order they were added.
    - `id` integer: Line id. Example: `1502`.
    - `itemId` integer, nullable: Catalog item id as used by `/api/v1/items/{id}`; null for a legacy free-text line.
    - `description` string: Line description (the item name when the line was added). Example: `Copper 12/2 Romex`.
    - `quantityOrdered` number, double: Quantity ordered. Example: `10`.
    - `quantityPicked` number, double: Quantity picked (scanned) so far. Example: `4`.
    - `quantityShipped` number, double: Quantity shipped so far. Example: `0`.
    - `unitPrice` number, double: Unit price, in the organization's currency. Example: `125.00`.
    - `unitCost` number, double: Unit cost, in the organization's currency; 0 when unknown. Example: `80.00`.
    - `lineTotal` number, double: Line total, `quantityOrdered` × `unitPrice`, in the organization's currency. Example: `1250.00`.
    - `notes` string, nullable: Line notes. Example: `Cut to 25 ft lengths`. Null when none.

## Other responses

- `400`: Missing `Idempotency-Key` or a malformed body (no lines, more than 200, values out of range).
- `401`: Unauthorized
- `403`: Forbidden
- `404`: No such sales order in this organization.
- `409`: The order is `Complete` or `Cancelled`, or the key's creator is unknown on this server.
- `422`: An unknown, deleted or draft item (`lines[i].itemId`) or lines totalling more than 1,000,000,000,000. Nothing was added.
- `429`: Too Many Requests

---

[API](https://skmtc.dev/flexonthejob/apis/flex-api.md) · [All operations](https://skmtc.dev/flexonthejob/apis/flex-api/llms.txt) · [OpenAPI document](https://skmtc.dev/flexonthejob/apis/flex-api/revisions/19c8f4d27acf?raw)
