Consultainer Docs

Endpoints

Time Entries

List, log, update, and delete time entries

List time entries

GET /api/v1/time-entries

Any role. Returns the key owner's own entries, newest date first, each with its project (including its client), task (or null) and attachments. Owners and admins can ask for someone else's with userId.

fromstringquery

YYYY-MM-DD, inclusive.

tostringquery

YYYY-MM-DD, inclusive.

projectIdstringquery
Only this project's entries.
userIdstringquery

Owner/admin only: another member's entries. Ignored for members.

curl "https://<your-instance>/api/v1/time-entries?from=2026-08-01&to=2026-08-31" \
  -H "Authorization: Bearer ch_live_..."

A from or to that isn't a date returns 422 with { "error": { "from": ["Use an ISO date, e.g. 2026-07-23."] } }.

{
  "timeEntries": [
    {
      "id": "cm9...",
      "date": "2026-08-05T00:00:00.000Z",
      "hours": "3.5",
      "description": "API migration",
      "billable": true,
      "rateOverride": null,
      "approvedAt": null,
      "invoiceLineItemId": null,
      "userId": "cm9...",
      "projectId": "cm9...",
      "taskId": null,
      "startTime": "09:00",
      "endTime": "12:30",
      "project": { "id": "cm9...", "name": "API migration", "client": { "id": "cm9...", "name": "Fintra Labs", "...": "..." }, "...": "..." },
      "task": null,
      "attachments": [
        {
          "id": "cm9...",
          "fileName": "Screenshot 2026-08-05 12.31.07.png",
          "contentType": "image/png",
          "sizeBytes": 184213,
          "clientVisible": true,
          "createdAt": "2026-08-05T16:31:09.000Z"
        }
      ]
    }
  ]
}

invoiceLineItemId is set once the entry is on an invoice. startTime and endTime are the local times the entry was worked (HH:MM, 24-hour), or null when only hours were logged; an endTime earlier than startTime ran past midnight.

Log time

POST /api/v1/time-entries

Any role. Returns 201 with { "timeEntry": { ... }, "warnings": [] }. Logging time can trigger budget alerts.

warnings lists anything worth a look that didn't stop the entry being saved: today, other timed entries of the same person that overlap this one, e.g. "Overlaps 09:00–10:30 on Acme · Website".

projectIdstringrequired

A project the key's owner can see.

taskIdstring
A task on that project.
datestringrequired

YYYY-MM-DD.

hoursnumber

Greater than 0, up to 24. Required unless startTime and endTime are given; then it's worked out from them, rounded to the organization's Round clock times to setting. If you send all three, hours can't be more than the time between start and end.

startTimestring

Local start time, HH:MM (24-hour). Send with endTime, or leave both out.

endTimestring

Local end time, HH:MM. Earlier than startTime means the next day (the entry keeps the start's date).

descriptionstring
Up to 2,000 characters.
billablebooleandefault: true
userIdstring

Owner/admin only: log the time for another member of the organization. Ignored for members.

rateOverridenumber

Owner/admin only: bill this entry at this hourly rate instead of the member's rate on the project. Ignored for members.

curl -X POST https://<your-instance>/api/v1/time-entries \
  -H "Authorization: Bearer ch_live_..." \
  -H "Content-Type: application/json" \
  -d '{"projectId": "cm9...", "date": "2026-08-05", "hours": 3.5, "description": "API migration"}'

With times instead of hours:

curl -X POST https://<your-instance>/api/v1/time-entries \
  -H "Authorization: Bearer ch_live_..." \
  -H "Content-Type: application/json" \
  -d '{"projectId": "cm9...", "date": "2026-08-05", "startTime": "09:00", "endTime": "12:30"}'

Update a time entry

PATCH /api/v1/time-entries/{id}

Takes the same body as logging time, and replaces the entry with it: send every field, not just the ones you're changing (projectId, date, and hours or startTime and endTime, are required; leaving out the times clears them). Returns { "timeEntry": { ... }, "warnings": [] }.

  • Members can update only their own entries; owners and admins can update anyone's, and can move an entry to another member with userId.
  • Moving an entry to a different projectId needs a project the key's owner can see.
  • When an owner or admin updates an entry without rateOverride, any existing override is cleared.

Delete a time entry

DELETE /api/v1/time-entries/{id}

Returns { "ok": true }. Members can delete only their own entries; owners and admins can delete anyone's.

Evidence (attachments)

Screenshots or PDFs attached to an entry as evidence. Anyone who can see the entry's project can list and download them; the entry's owner, and owners and admins, can add, share and delete them, even after the entry is invoiced.

List an entry's attachments

GET /api/v1/time-entries/{id}/attachments

Returns { "attachments": [ ... ] }, each as in the list above.

Add attachments

POST /api/v1/time-entries/{id}/attachments

Send multipart/form-data with one or more file parts (PNG, JPEG, GIF, WebP or PDF; checked by contents, up to 5MB each, or 25MB with S3 storage; 20 per entry). Add clientVisible=true to show them on the invoice's time detail. Returns 201 with { "attachments": [ ... ] }.

curl -X POST https://<your-instance>/api/v1/time-entries/cm9.../attachments \
  -H "Authorization: Bearer ch_live_..." \
  -F "file=@screenshot.png" -F "clientVisible=true"

Download an attachment

GET /api/v1/time-entries/{id}/attachments/{attachmentId}

Returns the file itself.

Share or unshare an attachment

PATCH /api/v1/time-entries/{id}/attachments/{attachmentId}

Body: { "clientVisible": true }. Returns { "attachment": { ... } }.

Delete an attachment

DELETE /api/v1/time-entries/{id}/attachments/{attachmentId}

Returns { "ok": true }.

StatusMessageFix
404Entry not found. / Attachment not found.Check the IDs. Entries on confidential projects need the key's owner on the project.
403You can only add evidence to your own time entries.Use the entry owner's key, or an owner or admin key.
422notes.png isn't a PNG, JPEG, GIF, WebP or PDF.Send a supported file.
422notes.png is over 5MB.Send a smaller file, or set up S3 storage.
422An entry can have up to 20 attachments.Delete some first.

Rules and errors

An entry can't be changed or deleted once it's on an invoice. If your organization requires timesheet approval, a member can't add, change or remove time in a week they've submitted or that's been approved; owners and admins can.

Every failure below returns 422 with { "error": "<message>" }, except validation errors, which return field errors such as { "error": { "hours": ["Hours must be greater than zero"] } }.

MessageFix
Project not found.Check projectId. Confidential projects need the key's owner on the project (this applies when moving an entry too).
That person isn't a member of this organization.userId must be a member of the key's organization.
Task not found.The task doesn't exist or is on a different project.
This client needs a start and end time on every entry. (as { "startTime": [...] })The project's client requires times: send startTime and endTime.
Hours can't be more than the time between start and end (2.5h). (as { "hours": [...] })Lower hours, or leave it out to have it worked out.
Entry not found.Check the entry ID.
You can only edit your own time entries. / You can only delete your own time entries.Use the entry owner's key, or an owner or admin key.
This entry has already been invoiced and can't be edited. (or deleted)If the invoice is still a draft, remove the entry's line item from it in the app first.
That week's timesheet is waiting for approval. Recall it to make changes.Recall the week on the Time page, or ask an admin.
That week's timesheet has been approved, so its time is locked.Ask an owner or admin to make the change.