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.
fromstringqueryYYYY-MM-DD, inclusive.
tostringqueryYYYY-MM-DD, inclusive.
projectIdstringqueryuserIdstringqueryOwner/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".
projectIdstringrequiredA project the key's owner can see.
taskIdstringdatestringrequiredYYYY-MM-DD.
hoursnumberGreater 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.
startTimestringLocal start time, HH:MM (24-hour). Send with endTime, or leave both
out.
endTimestringLocal end time, HH:MM. Earlier than startTime means the next day (the
entry keeps the start's date).
descriptionstringbillablebooleandefault: trueuserIdstringOwner/admin only: log the time for another member of the organization. Ignored for members.
rateOverridenumberOwner/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
projectIdneeds 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 }.
| Status | Message | Fix |
|---|---|---|
404 | Entry not found. / Attachment not found. | Check the IDs. Entries on confidential projects need the key's owner on the project. |
403 | You can only add evidence to your own time entries. | Use the entry owner's key, or an owner or admin key. |
422 | notes.png isn't a PNG, JPEG, GIF, WebP or PDF. | Send a supported file. |
422 | notes.png is over 5MB. | Send a smaller file, or set up S3 storage. |
422 | An 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"] } }.
| Message | Fix |
|---|---|
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. |