---
title: "Adds a labor line to a job."
method: POST
path: "/api/v1/jobs/{id}/labor"
tags: ["Jobs"]
---

# Adds a labor line to a job.

`POST /api/v1/jobs/{id}/labor`

## Request

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

Requires `ManageJobs` and an `Idempotency-Key` header. The customer rate is the
labor rate's hourly figure × `multiplier`; send `customerPrice` for $0-rate
variable-price labor. A cancelled job is read-only. The write is attributed to the key's creator.

## Path parameters

- `id` integer, required

## Request body

- AddJobLaborApiRequest: Body of `POST /api/v1/jobs/{id}/labor`: add a labor line.
  - `description` string, required: What the labor is, ≤ 200 characters. Required. Example: `Install and commission unit`.
  - `laborRateId` integer: Id of one of your organization's labor rates. The customer rate is that rate's hourly figure × `multiplier`.
  - `taskTemplateId` integer, nullable: Optional task template id (must belong to your organization).
  - `estimatedHours` number, double: Estimated hours, decimal ≥ 0. Example: `2.5`.
  - `multiplier` number, double: Multiplier applied to the labor rate's hourly rate (default 1). Example: `1.5` for overtime.
  - `customerPrice` number, double, nullable: Explicit per-job customer price for $0-rate variable-price labor, in the organization's currency, ≥ 0. Omit to bill rate × hours.
  - `notes` string, nullable: Line note, ≤ 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 job with this id in your organization.
- `422`: The labor rate or task template is unknown, or the job is cancelled (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)
