Skip to content
TruTecDevelopers
Get an API key

Timesheets

Read crew clock-ins and clock-outs, add missed shifts, correct punches with an audit trail, and approve or void time.

View as Markdown

A time entry is one clock-in and clock-out by one person on one work order (a scheduled visit; see Schedule & crews). Crews punch in the TruTec crew app; the office adds and corrects time. Approved, unvoided time is what payroll exports and job costing use.

The endpoints follow the Timesheets page in TruTec:

  • Every add, correction and void is written to the entry's audit trail with the reason.
  • Changing an approved entry clears its approval, so it must be approved again.
  • After a change, the job's labor cost is recalculated from the new hours.
  • Writes act as the person who created the API key. They need the Edit timesheets permission in TruTec to add, correct or void time, and Approve time to approve. Otherwise you get 403 naming the permission.

Times you send and the local_clock_in / local_clock_out you read are 24-hour times in the workspace's time zone. clock_in and clock_out are UTC with a Z suffix.

The time entry object#

Field Type Description
id integer
job_id, work_order_id integer The job and visit. work_order is {id, title}.
user_id, user integer, object The person, with {id, name}.
category string work, travel, break (unpaid) or shop.
paid boolean False for breaks.
date string The local day of the clock-in.
clock_in, clock_out string UTC. clock_out is null while the person is on the clock.
local_clock_in, local_clock_out string Local HH:MM.
hours number or null Clock time minus any unpaid lunch.
unpaid_lunch_minutes integer
wage_rate, wage_rate_kind, is_prevailing_wage The hourly cost rate captured at clock-in, and where it came from.
approved_at, approved_by Approval.
voided_at string or null Voided entries keep their row and history but count for nothing.
manual_entry string or null Set when a foreman or the office entered the time, with why.
edits array The audit trail (single-entry reads only): field, old_value, new_value, reason, edited_by, created_at.

Time entries#

List time entries#

GET /v1/time-entries

Permission: Read jobs

Ordered by clock-in, oldest first. Voided entries are left out unless you ask for them.

Name Type Required Description
start_date, end_date string No Local days of the clock-in.
user_id integer No One person.
job_id, work_order_id integer No One job or visit.
approved boolean No true or false.
open boolean No true for people still on the clock.
category string No work, travel, break or shop.
include_voided boolean No Default false.
limit, offset integer No Default 50, maximum 200.
curl "https://trutec.ai/api/v1/time-entries?start_date=2026-11-02&end_date=2026-11-08&approved=false" \
  -H "X-API-Key: sk_your_api_key_here"
{
  "data": [
    {
      "id": 602118,
      "job_id": 70412,
      "work_order_id": 4120,
      "user_id": 344,
      "category": "work",
      "paid": true,
      "clock_in": "2026-11-03T13:00:00Z",
      "clock_out": "2026-11-03T22:30:00Z",
      "hours": 9.0,
      "unpaid_lunch_minutes": 30,
      "wage_rate": 28.5,
      "wage_rate_kind": "standard",
      "is_prevailing_wage": false,
      "approved_at": null,
      "voided_at": null,
      "user": { "id": 344, "name": "Crew Lead" },
      "work_order": { "id": 4120, "title": "Sealcoat front lot" },
      "date": "2026-11-03",
      "local_clock_in": "07:00",
      "local_clock_out": "16:30",
      "approved_by": null,
      "manual_entry": null
    }
  ],
  "pagination": { "total": 1, "limit": 50, "offset": 0 }
}

Time entries have no updated_at. To sync time, pull a date range again, or use the bulk export.

Get a time entry#

GET /v1/time-entries/{id}

Permission: Read jobs

Includes edits, the entry's audit trail.

Add a time entry#

POST /v1/time-entries

Permission: Write jobs (and Edit timesheets in TruTec)

Adds a shift the crew app did not record: a paper timecard, a dead phone, a missed punch. The wage rate is captured the same way as a real clock-in. The entry lands unapproved, with no GPS, and with a "created" audit row.

Name Type Required Description
user_id integer Yes A TruTec login in the workspace.
work_order_id integer Yes The visit the time is for.
date string Yes Local day of the start.
clock_in, clock_out string Yes Local HH:MM. A clock_out before the start rolls to the next day for shifts up to 14 hours.
category string No Default work.
reason string Yes Kept in the audit trail, for example "paper timecard".
curl -X POST "https://trutec.ai/api/v1/time-entries" \
  -H "X-API-Key: sk_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 344,
    "work_order_id": 4120,
    "date": "2026-11-03",
    "clock_in": "07:00",
    "clock_out": "15:30",
    "reason": "Paper timecard"
  }'

Returns 201 with the entry, including edits.

Errors: 400 for a missing field, a bad time or an end before the start. 404 when the user or work order is not in your workspace.

Correct a time entry#

PATCH /v1/time-entries/{id}

Permission: Write jobs (and Edit timesheets)

Name Type Required Description
date string No Move the whole shift to another local day.
clock_in, clock_out string No Local HH:MM. Sending clock_out for an open entry closes it.
category string No
reason string No Kept with each change in the audit trail.

Each changed field gets an audit row and the entry's approval is cleared. Shifts longer than 24 hours are refused. Voided entries must be restored first (400).

Approve or unapprove#

POST /v1/time-entries/{id}/approve

POST /v1/time-entries/{id}/unapprove

Permission: Write jobs (and Approve time)

Open shifts (no clock-out) and voided entries cannot be approved (400).

Void or restore#

POST /v1/time-entries/{id}/void with {"reason": "Duplicate punch"}

POST /v1/time-entries/{id}/restore

Permission: Write jobs (and Edit timesheets)

Voiding never deletes: the entry stays with its history, stops counting toward hours, payroll and job cost, and loses its approval. A reason is required to void.