Skip to content
TruTecDevelopers
Get an API key

Holds & schedule requests

Pencil a date onto the schedule before the work is won, turn it into a real visit, and handle teammates' requests for a date, a move or a skipped day.

View as Markdown

Two ways to get work onto the schedule that the Schedule & crews endpoints don't cover: holds, for dates you want to keep free before a job is priced or won, and schedule requests, where someone who can't edit the schedule (or doesn't want to) asks the people who can.

Writes are made as the person who created the API key, with their own TruTec permissions, and need operations turned on for the workspace (403 Operations not enabled otherwise). Holds and approvals need permission to edit the schedule (403 view_only otherwise); anyone in operations can make a request.

Holds#

A hold is a tentative visit: a job name, an address, a line of scope and a date (and optionally a crew and times). With a crew, the hold locks those crew days so nothing else is scheduled there. A hold never publishes to crews, is never dispatched, and never tells the customer.

Holds are work orders with is_hold: true, so GET /v1/work-orders?include_holds=true and GET /v1/work-orders/{id} read them too. The hold object is the work order object plus:

Field Type Description
locked_days array Crew days this hold blocks. Empty without a crew.
job_is_placeholder boolean The job was made for the hold. Linking the hold to the real job merges it away.

List holds#

GET /v1/schedule/holds

Permission: Read jobs

Takes start_date, end_date (a hold is included when any of its days falls in the range), crew_id, job_id, limit and offset. Ordered by date.

Hold a date#

POST /v1/schedule/holds

Permission: Write jobs

Field Type Required Description
scheduled_date string Yes The day to hold.
job_id integer One of job_id or name Hold the date for an existing job.
name string One of job_id or name Customer or job name. Makes a lightweight job in the first stage of your sales pipeline, so it stays out of the schedule queue until sales moves it.
scope string No What the work is. The first line becomes the hold's title.
address string No Defaults to the job's address.
estimated_days number No Days on site, 0.5 to 365. Default 1.
start_time, end_time string No Local times like "07:00". A finish before the start runs past midnight.
crew_id integer No Locks this crew's days.
override_lock boolean No Owners only: hold a crew day that is already locked.
curl -X POST "https://trutec.ai/api/v1/schedule/holds" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Ridgeview HOA", "address": "400 Ridgeview Pkwy, Austin, TX", "scope": "Mill and overlay, phase 1", "scheduled_date": "2026-11-09", "estimated_days": 3, "crew_id": 18 }'

Returns 201 with the hold as data, job_created (a placeholder job was made) and same_day_work_order_ids (other work already on that crew day: a heads-up, not a block). A crew day that is already held or locked returns 409 with the locks in the way. Workspaces whose jobs come from an outside system must pass job_id.

Change a hold#

PATCH /v1/schedule/holds/{id}

Permission: Write jobs

Takes title, scope, address, scheduled_date, estimated_days, start_time, end_time, crew_id and override_lock. Only the fields sent change. Moving it or changing its crew re-checks the locks (409 when that crew day is taken) and moves its locks along.

Turn a hold into a visit#

POST /v1/schedule/holds/{id}/convert

Permission: Write jobs

The work was won: the hold becomes a real visit on the same day and crew, and its locks are released. It is a draft until you publish the schedule, like any new visit. Returns the work order.

Tie a hold to the real job#

POST /v1/schedule/holds/{id}/link with {"job_id": 70412}

Permission: Write jobs

The hold moves onto that job and keeps its date, crew, times and locks. When the hold sat on a placeholder job made for it (and nothing else uses it), the placeholder is merged into the real job, its notes, activity and files included, and removed; merged_placeholder_job names it.

Release a hold#

DELETE /v1/schedule/holds/{id}

Permission: Write jobs and Delete records

Lets the date go: the hold and its crew-day locks are removed. The job stays. Returns 204.

Schedule requests#

A request asks the people who can edit the schedule for one of three things:

kind Asks for Needs
date A date for a whole job, or one service line of it job_id, requested_date, optionally estimate_service_id
move Moving a scheduled visit to another date work_order_id, requested_date
skip One day of a multi-day visit off (not its first day) work_order_id, skip_date

Approving does exactly what the scheduler's own action would. Everyone who can edit the schedule gets an in-app notification of a new request; the requester gets one when it is decided. Nothing reaches customers or crews until the schedule is published.

The request object#

Field Type Description
id integer Request id.
kind string date, move or skip.
status string pending, approved or declined.
summary string One plain line, for example "Move Sealcoat: Wed Nov 4 → Thu Nov 5".
job_id, job integer, object The job.
requested_date, skip_date string The date asked for (for skip, both are the day off).
estimate_service_id, service_name The service line, for a date request about one line.
work_order_id, work_order_title The visit, for move and skip.
note string or null The requester's note. Approving adds it to the visit's dispatch notes.
requested_by, decided_by object or null {id, name}.
decided_at, decision_note, created_at

List requests#

GET /v1/schedule/date-requests

Permission: Read jobs

Takes status, kind, job_id, work_order_id, limit and offset. Newest first.

Make a request#

POST /v1/schedule/date-requests

Permission: Write jobs

Takes the fields in the table above plus note. Service lines that can be requested are the job's unscheduled lines (estimate_service_id from GET /v1/schedule/queue); a line already on the calendar is 400 (ask to move its visit instead). A request still waiting for the same thing is 409.

curl -X POST "https://trutec.ai/api/v1/schedule/date-requests" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "move", "work_order_id": 4120, "requested_date": "2026-11-05", "note": "Tenant asked for Thursday" }'

Returns 201 with the request.

Approve or decline#

POST /v1/schedule/date-requests/{id}/approve

POST /v1/schedule/date-requests/{id}/decline

Permission: Write jobs

Both take an optional note the requester sees. Approve places the work on the date, moves the visit or skips the day, with the same crew-day locks and rules as a drag on the Schedule (409 when one gets in the way), and returns the request plus the work_order it changed. Decline changes nothing on the schedule. A request that was already decided is 409.