Okouro docs

A timesheet covers one consultant over one period. Each timesheet holds one or more lines, one per assignment, and each line records a status for every day in the period. The record moves through a fixed lifecycle: draft, submitted, then validated or rejected.

Lifecycle

draft  --submit-->  submitted  --validate-->  validated
  ^                     |
  +------reject---------+   (rejected is editable again, like draft)
  • draft and rejected are editable.
  • submitted is locked pending review.
  • validated is final.

Who does what

ActionRole
Create a timesheet, add a line, delete a draftAdmin
Mark days, edit a line commentThe owning consultant or an admin, while draft/rejected
SubmitThe owning consultant, from draft/rejected
Validate or rejectAdmin (or the consultant’s team leader)

1. Create the timesheet

An admin creates the sheet for a consultant and a period. The period dates follow the account’s cadence (see Conventions).

POST /timesheets HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "consultant_id": "0190a1c2-...",
  "period_start": "2026-01-01",
  "period_end": "2026-01-31"
}

Returns 201 Created with a TimesheetDetailResponse - the timesheet plus its lines. A sheet already covering that consultant and period returns 409 Conflict.

2. Add a line per assignment

Each line ties the timesheet to one assignment (a consultant on a mission). See Run a mission to create assignments.

POST /timesheets/{timesheet_id}/lines HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{ "assignment_id": "0190a1c2-..." }

Returns 201 Created. The assignment must belong to the same consultant; otherwise you get 422.

3. Mark days

For each day the consultant worked, set a status on the line. status is worked, half_day, or not_worked.

PUT /timesheets/{timesheet_id}/lines/{line_id}/day HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{ "date": "2026-01-05", "status": "worked" }

Returns 200 OK with the updated line. A date outside the period returns 422. The line’s work_day_count is derived from its days (a worked day counts 1, half_day counts 0.5).

Optionally add a comment to the line:

PUT /timesheets/{timesheet_id}/lines HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{ "line_id": "0190a1c2-...", "comment": "Client onboarding sprint" }

4. Submit

The consultant submits the finished sheet. It becomes read-only.

POST /timesheets/{timesheet_id}/submit HTTP/1.1
Authorization: Bearer <access_token>

Returns 200 OK with the TimesheetResponse (status now submitted). Submitting a sheet that is not draft/rejected returns 409.

5. Validate or reject

An admin or the consultant’s team leader decides.

POST /timesheets/{timesheet_id}/validate HTTP/1.1
Authorization: Bearer <access_token>
POST /timesheets/{timesheet_id}/reject HTTP/1.1
Authorization: Bearer <access_token>
Content-Type: application/json

{ "reason": "Missing the Friday entry" }

Validate moves the sheet to validated (final). Reject moves it to rejected with a reason, and it becomes editable again so the consultant can fix and resubmit.

Reading timesheets

GET /timesheets lists them, paged and filterable by consultant_id, status, period_start/period_end, search, and scope (mine or team). GET /timesheets/{id} returns the full detail with lines and days.

A line looks like this (note billing_rate is null for consultant-role callers):

{
  "id": "0190a1c2-...",
  "timesheet_id": "0190a1c2-...",
  "assignment_id": "0190a1c2-...",
  "mission_name": "Apollo",
  "billing_rate": "900.00",
  "work_day_count": "21",
  "comment": "Client onboarding sprint",
  "days": [
    { "date": "2026-01-05", "status": "worked" },
    { "date": "2026-01-06", "status": "half_day" }
  ]
}

Endpoints at a glance

ActionMethodPathSuccess
ListGET/timesheets200
Get detailGET/timesheets/{id}200
CreatePOST/timesheets201
Add linePOST/timesheets/{id}/lines201
Set a dayPUT/timesheets/{id}/lines/{line_id}/day200
Edit linePUT/timesheets/{id}/lines200
SubmitPOST/timesheets/{id}/submit200
ValidatePOST/timesheets/{id}/validate200
RejectPOST/timesheets/{id}/reject200
Delete draftDELETE/timesheets/{id}204