Skip to content
TruTecDevelopers
Get an API key

Job operations & materials

Move a won job through your operational stages, record material orders and supplier availability, tick off supplier confirmations, and move cards on the Operations Pipeline.

View as Markdown

Once a job is won, the office takes it through the workspace's operational stages (for example Prepare, Execute, Close out), orders its materials, and confirms supply before the crew arrives. These endpoints read and change that record, the same way the job's Operations view does in TruTec.

Writes are made as the person who created the API key, need permission to edit the schedule (403 view_only otherwise) and operations turned on for the workspace (403 Operations not enabled). Material order and supplier records are office notes: nothing here contacts a supplier.

Revisions. The job's records are shared by everyone in the office, so each write carries the revision you read, and a stale one returns 409 instead of overwriting someone else's change. There are three, all in GET /v1/jobs/{id}/operations:

Revision Send it with
revision /operations/stage (and optionally /operations/handoff)
material_orders.revision the three material-orders actions
supply_confirmations.revision supply-confirmations confirm and undo

On 409, read the job again and retry with the new revision.

Get a job's operations status#

GET /v1/jobs/{id}/operations

Permission: Read jobs

{
  "data": {
    "job_id": 70412,
    "job": { "id": 70412, "name": "Northgate Plaza reseal" },
    "stage": "prep",
    "stage_label": "Prepare",
    "revision": 1,
    "handed_off_at": "2026-10-20T15:44:02Z",
    "stage_entered_at": "2026-10-20T15:44:02Z",
    "sales_status": "won",
    "handoff_sales_status": "won",
    "can_hand_off": false,
    "stages": [
      { "key": "prep", "label": "Prepare", "color": "#f59e0b" },
      { "key": "execution", "label": "Execute", "color": "#3b82f6" },
      { "key": "closeout", "label": "Close out", "color": "#10b981" }
    ],
    "material_orders": {
      "revision": 2,
      "estimate_name": "Northgate reseal",
      "total": 1,
      "fully_ordered": 1,
      "received": 0,
      "complete": true,
      "items": [
        {
          "key": "4f0c2a9e1b7d3c6a8e5f1a2b",
          "name": "Sealer",
          "unit": "gal",
          "quantity": 1200.0,
          "status": "ordered",
          "status_label": "Ordered",
          "supplier": "Local supplier",
          "po": "PO-1042",
          "expected_on": "2026-11-02",
          "ordered_quantity": 1200.0,
          "received_quantity": 0.0,
          "fully_ordered": true,
          "estimate_changed": false,
          "special_load_out": false,
          "scope": "9b1e0c4d2a7f5e3c1b8d6a0e",
          "availability_scope": "availability-v1:…",
          "availability": { "state": "confirmed", "label": "Supplier availability confirmed", "supplier": "Local supplier", "fulfillment": "delivery", "confirmed_on": "2026-11-02", "confirmed_time": "07:00", "available_quantity": 1200.0 },
          "updated_by": "Office Manager",
          "updated_at": "2026-10-28T16:02:11Z"
        }
      ]
    },
    "supply_confirmations": {
      "revision": 0,
      "total": 1,
      "open": 1,
      "overdue": 0,
      "due_today": 0,
      "items": [
        {
          "key": "c71d5e0a9b3f2e8d4a6c1b0f",
          "kind": "material",
          "name": "Hot mix asphalt",
          "unit": "ton",
          "quantity": 180.0,
          "lead_days": 2,
          "work_date": "2026-11-09",
          "due_date": "2026-11-05",
          "due_text": "2 business days before the work on Mon, Nov 9",
          "status": "upcoming",
          "status_label": "Upcoming",
          "recheck": null,
          "confirmed_by": null,
          "confirmed_at": null,
          "note": null
        }
      ]
    }
  }
}

Materials come from the job's awarded estimate (the latest saved one before it is awarded); materials the catalog marks as not purchased, or below their order-above quantity, are left out. estimate_changed means the estimate's quantity changed after the order was recorded, so the item counts as not ordered until you record it again. unit_price appears in availability only for keys whose creator can see prices.

Supply confirmations are the catalog items set to "confirm with the supplier N business days before the work". work_date is the first scheduled day of the work that uses the item, due_date the day the confirmation is due. A confirmation goes back to open (with recheck saying why) when the work moves or the quantity changes.

Stages#

Hand a won job to operations#

POST /v1/jobs/{id}/operations/handoff

Permission: Write jobs

Like Start operations workflow in TruTec: the job enters the first operational stage and that stage's automations run (tasks, checklists and in-app notices the owner set up). The job must be in the sales stage set for handoff (handoff_sales_status), otherwise 409. A job already in operations comes back unchanged. Takes an optional revision. Returns the operations status.

Move a job to another stage#

POST /v1/jobs/{id}/operations/stage with {"stage": "execution", "revision": 1}

Permission: Write jobs

stage is a key from stages. That stage's automations run once. Returns the operations status.

Material orders#

The three actions take the material's key in the path and material_orders.revision in the body. You may also send the item's scope as you read it; if the estimate changed since, the response is 409.

Record an order#

POST /v1/jobs/{id}/material-orders/{key}/order-status

Permission: Write jobs

Field Type Required Description
status string Yes unconfirmed, partially_ordered, ordered, partially_received or received.
supplier string Unless unconfirmed Up to 200 characters.
po string No Purchase order or confirmation number.
expected_on string No Expected delivery date.
ordered_quantity number For partial orders Total ordered so far. Defaults to the full quantity.
received_quantity number For partial deliveries Total received so far.
revision integer Yes material_orders.revision.

The quantities must agree with the status (for example ordered means the full quantity is ordered and none received); otherwise 400 says what's wrong.

Record a supplier availability check#

POST /v1/jobs/{id}/material-orders/{key}/availability

Permission: Write jobs

Field Type Required Description
availability_state string Yes not_requested, pending, confirmed or unavailable.
supplier string Unless not_requested
contact, notes string No Who you spoke to, and anything worth keeping.
fulfillment string No delivery (default) or pickup.
requested_on, requested_time string No When you need it.
confirmed_on, confirmed_time, available_quantity When confirmed What the supplier confirmed.
unit_price number No Dollars. Needs a key whose creator can see prices (403 otherwise).
revision integer Yes material_orders.revision.

Order quantities and status don't change.

Record what it actually cost#

POST /v1/jobs/{id}/material-orders/{key}/actual-cost

Permission: Write jobs

Field Type Required Description
actual_cost number Yes Dollars, up to 2 decimals.
incurred_on string Yes Date on the invoice or ticket.
cost_reference string Yes The supplier invoice or delivery-ticket number.
revision integer Yes material_orders.revision.

Adds a material cost to the job's costing, pending review, and returns its cost_entry_id with the operations status. The same material and reference twice is 409. Needs estimating and a key whose creator can see prices.

Supply confirmations#

POST /v1/jobs/{id}/supply-confirmations/{key}/confirm with {"revision": 0, "note": "Plant confirmed 180 t for 7am"}

POST /v1/jobs/{id}/supply-confirmations/{key}/undo with {"revision": 1}

Permission: Write jobs

Confirm records that you confirmed the item with the supplier for its current work date and quantity (a note up to 300 characters is optional). Undo puts it back to open. Both take supply_confirmations.revision; if the work date or quantity changed since you read it, the response is 409.

Operations Pipeline#

The Operations Pipeline board shows every visit (and won work waiting to be scheduled) as a card in a column: Ready to schedule, Scheduled, Crew Dispatched, In progress, Completed and Invoiced, plus any columns the owner added between them.

List the columns#

GET /v1/operations-pipeline/columns

Permission: Read jobs

{
  "data": [
    { "key": "ready", "label": "Ready to schedule", "color": "#94a3b8", "custom": false, "can_drop_here": true, "moves_here_when": "A job is won on the Sales Pipeline." },
    { "key": "c_1a2b3c4d", "label": "Waiting on permit", "color": "#64748b", "custom": true, "can_drop_here": true, "moves_here_when": "You drag a card here." },
    { "key": "scheduled", "label": "Scheduled", "color": "#60a5fa", "custom": false, "can_drop_here": true, "moves_here_when": "A service is put on the Schedule." }
  ]
}

Move a card#

POST /v1/operations-pipeline/move

Permission: Write jobs

Like dragging a card on the board.

Field Type Required Description
work_order_id integer One of these A visit.
queue_id string One of these Won work not yet scheduled, from GET /v1/schedule/queue ("es-5512", "job-70412").
to string Yes A column key.
scheduled_date string To scheduled The day.
crew_id integer or null No With scheduled: the crew.

What each move does:

  • One of the owner's columns: the card is parked there and that column's automations run once for the job. Nothing on the schedule changes.
  • ready: unschedules the visit (or moves a parked card back).
  • scheduled: puts it on scheduled_date (draft until published). Queued work becomes a visit.
  • done: completes the visit.
  • sent is refused (400): moving a card to Crew Dispatched sends it to the crew, so use send-dispatch instead. in_progress and invoiced move by themselves (the crew clocks in; an invoice is created).

Moves the board doesn't allow return 400 saying why. Returns the card's column, parked_from (for an owner's column, the column its events put it in) and the work_order, if there is one.