Time Tracking
Public API

Approve Timesheet

Changed on

Approves a timesheet (process resource). Only a timesheet whose derived status is PENDING_APPROVAL can be approved; approving an already-approved or not-yet-open timesheet returns 409. Returns the updated timesheet with status APPROVED.

OAuth Scopes: time_tracking:timesheets.write

post/api/v1/time-tracking/timesheet-approvals

Request

Request body

timesheetIdinteger required

ID of the timesheet to approve.

lastChangedAtstring date-time required

The timesheet's hoursLastChangedAt value as last fetched by the caller, in ISO 8601 UTC. This is specifically hoursLastChangedAt, not updatedAt: hours are stored separately, so the timesheet row's updatedAt may not move when hours change (and vice versa). The approval is rejected with 409 if the timesheet's hours changed after this instant, so callers never approve a version they have not seen.

Example request

{
  "timesheetId": 100,
  "lastChangedAt": "2026-03-29T17:00:00Z"
}

Response

Successfully approved the timesheet.

idinteger

Identifier for the timesheet.

employeeIdinteger

The employee this timesheet belongs to.

type'SINGLE' | 'CLOCK' | 'MULTIPLE' | 'HOUR'

Tracking method snapshotted from the configuration at period start.

startDatestring date

First date of the pay period (inclusive).

endDatestring date

Last date of the pay period (inclusive).

status'OPEN' | 'PENDING_APPROVAL' | 'APPROVED'

Derived approval state.

totalHoursnumber float

Sum of all hours across regular, overtime, holiday, and approved PTO.

overtimeHoursnumber float

Subset of totalHours that falls into the overtime rate bucket.

approvedByinteger nullable

The user who approved the timesheet. Null until approved.

approvedAtstring date-time nullable

When the timesheet was approved. Null until approved.

createdAtstring date-time

When the timesheet record was created.

updatedAtstring date-time

When the timesheet was last modified.

hoursLastChangedAtstring date-time

When the timesheet's hours were last changed. Send this value back as the approval request's lastChangedAt. Distinct from updatedAt because hours are stored separately from the timesheet row.

Example response

{
  "id": 100,
  "employeeId": 42,
  "type": "CLOCK",
  "startDate": "2026-03-16",
  "endDate": "2026-03-29",
  "status": "PENDING_APPROVAL",
  "totalHours": 80,
  "createdAt": "2026-03-16T00:00:00Z",
  "updatedAt": "2026-03-29T17:00:00Z",
  "hoursLastChangedAt": "2026-03-29T17:00:00Z"
}

Changes