---
title: "Edits a material line's quantity, price and/or note."
method: PATCH
path: "/api/v1/jobs/{id}/items/{itemId}"
tags: ["Jobs"]
---

# Edits a material line's quantity, price and/or note.

`PATCH /api/v1/jobs/{id}/items/{itemId}`

## Request

- Base URL: `https://app.flexonthejob.com`
- URL: `https://app.flexonthejob.com/api/v1/jobs/{id}/items/{itemId}`
- Auth: HTTP bearer

Requires `ManageJobs` and an `Idempotency-Key` header. PATCH semantics: only the
fields you send change. Quantity changes only apply while the line is Planned (or a
counter-sales line) and can't drop below the delivered amount; price edits are kept as a
manual override unless they equal the resolved price (send `clearPriceOverride` to
revert). A configured (add-on) line's price is changed through the app. Lines on a
finalized invoice change only through Return Items. Notes may be edited even on billed lines.

## Path parameters

- `id` integer, required
- `itemId` integer, required

## Request body

- UpdateJobItemApiRequest: Body of `PATCH /api/v1/jobs/{id}/items/{itemId}`: partial edit of a material line. Omitted fields are left unchanged.
  - `quantity` integer, nullable: New quantity, ≥ 1. Only Planned lines (or counter-sales lines) change; can't drop below what was already delivered. Omit to leave unchanged.
  - `unitPrice` number, double, nullable: New unit price, ≥ 0, kept as a manual override unless it equals the resolved price. Omit to leave unchanged; set `clearPriceOverride` to revert to the tier/base price instead.
  - `clearPriceOverride` boolean: Revert the line to its tier/base-resolved price (clears any manual override). Takes precedence over `unitPrice`. Default `false`.
  - `notes` string, nullable: New line note. Omit to leave unchanged; send an empty string to clear it. ≤ 500 characters.

## Response `200`

The updated job.

- JobDetailDto: Full job as returned by `GET /api/v1/jobs/{id}`: everything in `JobSummaryDto` plus the job site, pricing breakdown, assignees, material and labor lines and the note/update timeline.
  - `id` integer: Numeric id used in `/api/v1/jobs/{id}`. Unique within this organization. Example: `1042`.
  - `syncId` string, uuid: Stable cross-device identifier (GUID) — also the pagination cursor value. Example: `3f2504e0-4f89-11d3-9a0c-0305e82c3301`.
  - `jobNumber` string: Human-readable job number shown to staff and customers. Example: `J-2026-0042`.
  - `jobType` string, nullable: Free-text job category configured by the organization. Example: `Installation`, `Repair`.
  - `status` string: Workflow status name. One of `Draft`, `InspectionInProgress`, `Estimated`, `AwaitingOptionSelection`, `Scheduled`, `InProgress`, `Completed`, `Invoiced`, `Paid`, `Cancelled`, `Outstanding`.
  - `statusName` string: Display label of the status as the organization has customised it (falls back to `status`). Example: `On Site`.
  - `paymentStatus` string, nullable: Payment state, independent of workflow status: `Unpaid`, `Partial`, `Paid`, `Overpaid` or `Refunded`.
  - `customerId` integer: Id of the customer the job is for (see `/api/v1/customers/{id}`).
  - `customerName` string: Customer display name at read time. Example: `Acme Property Management`.
  - `description` string, nullable: Short description of the work. May be null for drafts.
  - `createdAt` string, date-time: When the job was created, ISO-8601 UTC. Example: `2026-09-01T14:03:22Z`.
  - `updatedAt` string, date-time: Last modification time, ISO-8601 UTC (creation time when never modified). Use with the `updatedSince` filter to poll for changes.
  - `scheduledDate` string, date-time, nullable: Scheduled start as the organization's local wall-clock time with no UTC offset (the `date-time` format here is a local time, not RFC 3339 with zone), e.g. `2026-09-08T08:30:00`. Null when unscheduled.
  - `scheduledDurationMinutes` integer, nullable: Planned duration in minutes (default 120 when scheduled without an explicit duration). Null when unscheduled.
  - `startDate` string, date-time, nullable: When work actually started, ISO-8601 UTC. Null until the job is in progress.
  - `completedDate` string, date-time, nullable: When work was completed, ISO-8601 UTC. Null until completed.
  - `totalPrice` number, double: Customer price before tax, after the job-level discount, in the organization's currency (decimal, e.g. `1250.00`).
  - `notes` string, nullable: Internal notes typed on the job itself (not the timeline). Null when empty.
  - `siteAddress` JobSiteAddressDto: A customer address used as the job site.
    - `id` integer: Customer address id (see `/api/v1/customers/{id}/locations`).
    - `name` string: Label the customer gave the address. Example: `Main Office`.
    - `address` string, nullable: Street line 1.
    - `address2` string, nullable: Street line 2 (suite, unit).
    - `city` string, nullable: City.
    - `state` string, nullable: State / province.
    - `postalCode` string, nullable: Postal / ZIP code.
    - `country` string, nullable: Country, when recorded.
  - `materialPrice` number, double: Sum of material lines before job-level discount and tax (decimal currency, e.g. `800.00`).
  - `laborPrice` number, double: Sum of labor lines before job-level discount and tax (decimal currency).
  - `customChargesTotal` number, double: Sum of custom charges before job-level discount and tax (decimal currency).
  - `discountPercent` number, double, nullable: Job-level discount as a percentage of every line (0–100), or null when a fixed amount / no discount applies.
  - `discountAmount` number, double, nullable: Job-level fixed discount amount (decimal currency), or null when a percentage / no discount applies.
  - `taxRate` number, double: Tax rate locked in at creation, as a percentage (e.g. `8.25`).
  - `downPaymentAmount` number, double, nullable: Money collected before invoicing (decimal currency), or null when none.
  - `assignees` JobAssigneeDto[]: Staff assigned to the job (requires the Scheduling module to change). Empty when unassigned.
    - `userId` string: Identity user id of the assignee (matches `/api/v1/users`).
    - `name` string: Display name (first + last, or email when no name is set). Example: `Maria Lopez`.
    - `scheduleGroupId` integer, nullable: Schedule group (crew) the assignment was made under, or null.
    - `scheduleGroupName` string, nullable: Display name of `scheduleGroupId`. Example: `Crew A`.
    - `notes` string, nullable: Note attached to the assignment, if any.
  - `items` JobMaterialLineDto[]: Material lines (parts/products) on the job, excluding removed lines.
    - `id` integer: Line id.
    - `syncId` string, uuid: Stable cross-device identifier of the line.
    - `itemTemplateId` integer: Item template (catalog product) id — see `/api/v1/items/{id}`.
    - `itemTemplateName` string: Catalog product name at read time. Example: `Torsion Spring 2in x 32in`.
    - `quantity` integer: Quantity ordered for the job (whole units).
    - `deliveredQuantity` integer: Quantity already delivered to / used at the site.
    - `unitPrice` number, double: Customer unit price before line discount (decimal currency).
    - `discountPercent` number, double: Line discount as a percentage (0–100).
    - `discountAmount` number, double: Line discount as a fixed amount (decimal currency).
    - `totalPrice` number, double: Extended customer price after line discount, before job discount and tax (decimal currency).
    - `status` string: Fulfilment state: `Planned`, `Reserved`, `Used` (delivered) or `Returned`.
    - `notes` string, nullable: Line note, or null when none. Example: `Bring the 8-ft ladder`.
  - `laborItems` JobLaborLineDto[]: Labor lines on the job, excluding removed lines.
    - `id` integer: Line id.
    - `syncId` string, uuid: Stable cross-device identifier of the line.
    - `description` string: What the work is. Example: `Replace torsion springs`.
    - `estimatedHours` number, double: Hours quoted (decimal, e.g. `1.5`).
    - `actualHours` number, double: Hours actually worked (decimal). Zero until logged.
    - `customerRate` number, double: Hourly rate billed to the customer (decimal currency).
    - `customerTotal` number, double: Customer price for the line after line discount, before job discount and tax (decimal currency).
    - `technicianId` string, nullable: Identity user id of the technician the line is booked to, or null.
    - `status` string: Line state name from the LaborItemStatus enum (e.g. `Planned`, `Scheduled`, `Completed`).
    - `notes` string, nullable: Line note, or null when none. Example: `Overtime rate approved by owner`.
  - `customCharges` JobCustomChargeDto[]: Custom charges (fees, disposal, permits …) on the job, excluding removed charges. Edit or remove one with its `id` via `PATCH`/`DELETE /api/v1/jobs/{id}/charges/{chargeId}`.
    - `id` integer: Charge id — pass as `chargeId` to `PATCH`/`DELETE /api/v1/jobs/{id}/charges/{chargeId}`. Example: `4210`.
    - `syncId` string, uuid: Stable cross-device identifier of the charge.
    - `name` string: Charge name shown on the job and invoice. Example: `Disposal fee`.
    - `description` string, nullable: Longer description of the charge, or null when none. Example: `Haul-away of the old unit`.
    - `price` number, double: Price charged to the customer before discounts, in the organization's currency (e.g. `75.00`).
    - `discountPercent` number, double: Line discount as a percentage (0–100), applied before the flat discount.
    - `discountAmount` number, double: Flat discount taken off the charge after the percentage discount, in the organization's currency.
    - `effectivePrice` number, double: Price after both line discounts, in the organization's currency — the amount that rolls into `customChargesTotal`. Example: `67.50`.
  - `updates` JobNoteDto[]: Timeline of notes, progress updates and status changes, oldest first.
    - `id` integer: Entry id.
    - `syncId` string, uuid: Stable cross-device identifier of the entry.
    - `type` string: Kind of entry — JobUpdateType name such as `GeneralNote`, `ProgressUpdate`, `Issue`, `CustomerRequest`, `StatusChange`, `Photo` or `Signature`.
    - `description` string: The note text.
    - `isInternal` boolean: True when the note is staff-only and must not be shown to the customer.
    - `createdAt` string, date-time: When the entry was written, ISO-8601 UTC.
    - `createdByName` string, nullable: Display name of the author, when recorded.
    - `oldStatus` string, nullable: For status-change entries: the previous status name; otherwise null.
    - `newStatus` string, nullable: For status-change entries: the new status name; otherwise null.
    - `attachmentName` string, nullable: For `Photo` / `Signature` entries: the attached image's file name (e.g. `J-1042-20260929-143015.jpg`); null for entries without an attachment. The image itself is viewable on the job's page in the web app and is included in the organization data export.
  - `warning` string, nullable: Set only on the `POST /api/v1/jobs` response when `publishImmediately` was requested but the job could not be published yet (it was created as a `Draft`), e.g. `Not published yet: Cannot finalize estimate. All material items must have a unit price.`. Always null on `GET`.

## Other responses

- `400`: Malformed body or missing `Idempotency-Key`.
- `401`: Unauthorized
- `403`: Forbidden
- `404`: No such job in your organization, or the line is not one of its live material lines.
- `422`: The job is cancelled, the line is billed or configured, or the quantity/price is not allowed (the detail says which).
- `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)
