A predictable REST API for time, catalog, billing, and reporting data. Machine-readable spec: /api/v1/openapi.json (imports straight into Postman).
Send Authorization: Bearer <token>. Two token kinds, both created on the in-app Developer page:
user_id.Endpoints marked manager+ or admin refuse lower-grade personal tokens — money (rates, invoices, reports) never reaches member tokens.
per_page, total_pages, total_entries, next_page, previous_page, and a links object.{"message": "…"} with meaningful status codes; business rules refuse with 422.Retry-After).Idempotency-Key header (any unique string, ≤255 chars) with POSTs. Retrying with the same key and payload within 24 hours replays the original response (Idempotency-Replayed: true) instead of creating a duplicate; the same key with a different payload is refused with 422.approval_status maps open and rejected weeks to unsubmitted.The authenticated workspace.
GET/v1/companyRetrieve the company{
"name": "Fieldnote Studio",
"is_active": true,
"base_uri": "https://fieldnote.timetrakkr.com",
"full_domain": "fieldnote.timetrakkr.com",
"week_start_day": "Monday",
"currency": "USD",
"time_zone": "America/New_York"
}ERRORS{
"message": "Project not found"
}Tracked time. Member-grade personal tokens see and modify only their own entries; submitted or approved weeks are locked.
GET/v1/time_entriesList time entriesfrom — Only entries on/after this date (YYYY-MM-DD)to — Only entries on/before this dateuser_id — Filter to one memberclient_id — Filter by clientproject_id — Filter by projecttask_id — Filter by taskbillable — true/falseis_billed — true = already invoicedis_running — true = timer currently runningapproval_status — unsubmitted, submitted, or approvedexternal_reference_id — Match an external referenceupdated_since — ISO 8601 datetimepage — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"time_entries": [
{
"id": "3f0a1c2e-5b6d-4e7f-8a90-1b2c3d4e5f60",
"spent_date": "2026-08-03",
"hours": 2,
"notes": "Homepage hero — second revision round",
"billable": true,
"is_locked": false,
"is_billed": false,
"is_running": false,
"approval_status": "unsubmitted",
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair"
},
"project": {
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand"
},
"task": {
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design"
},
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"external_reference": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/time_entries?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/time_entries?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/time_entriesCreate a time entryPersonal tokens log as their owner. Weeks already submitted for approval refuse new time (422). Omitting `hours` (and the times) starts a running timer and returns the entry with `is_running` true. An agent member's entry needs started_at and ended_at, and is refused (422, with a `code`) if it overlaps that member's other entries, ends in the future, or takes its day past 24 hours.
BODYproject_id — UUID, requiredtask_id — UUID, requiredspent_date — YYYY-MM-DD, requiredhours — Decimal hours (rounded per org policy). OMIT to start a running timer instead.notes — Entry notesbillable — Defaults trueexternal_reference.id — External system reference — import provenance, unique per org (a reused one is 409)issue_key — Jira issue this entry is for, e.g. ACME-142. Several entries may share one.user_id — Required with an organization key; ignored for personal tokensstarted_at — ISO timestamp the work started (send with ended_at). Without hours, the duration comes from these.ended_at — ISO timestamp the work ended{
"id": "3f0a1c2e-5b6d-4e7f-8a90-1b2c3d4e5f60",
"spent_date": "2026-08-03",
"hours": 2,
"notes": "Homepage hero — second revision round",
"billable": true,
"is_locked": false,
"is_billed": false,
"is_running": false,
"approval_status": "unsubmitted",
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair"
},
"project": {
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand"
},
"task": {
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design"
},
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"external_reference": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}GET/v1/time_entries/{id}Retrieve a time entryid (required) — The time entry id (UUID){
"id": "3f0a1c2e-5b6d-4e7f-8a90-1b2c3d4e5f60",
"spent_date": "2026-08-03",
"hours": 2,
"notes": "Homepage hero — second revision round",
"billable": true,
"is_locked": false,
"is_billed": false,
"is_running": false,
"approval_status": "unsubmitted",
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair"
},
"project": {
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand"
},
"task": {
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design"
},
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"external_reference": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}PATCH/v1/time_entries/{id}Update a time entryLocked (approved/invoiced) and submitted entries return 422.
PARAMETERSid (required) — The time entry id (UUID)project_id — UUIDtask_id — UUIDspent_date — YYYY-MM-DD (target week must be open)hours — Decimal hoursnotes — Entry notesbillable — true/falseexternal_reference.id — Set or replace the external reference{
"id": "3f0a1c2e-5b6d-4e7f-8a90-1b2c3d4e5f60",
"spent_date": "2026-08-03",
"hours": 2,
"notes": "Homepage hero — second revision round",
"billable": true,
"is_locked": false,
"is_billed": false,
"is_running": false,
"approval_status": "unsubmitted",
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair"
},
"project": {
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand"
},
"task": {
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design"
},
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"external_reference": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}DELETE/v1/time_entries/{id}Delete a time entryid (required) — The time entry id (UUID){
"message": "Project not found"
}PATCH/v1/time_entries/{id}/restartRestart the timer on an entry · personal tokenPersonal tokens only — timers belong to a person. Stops any other running timer.
PARAMETERSid (required) — The time entry id (UUID){
"id": "3f0a1c2e-5b6d-4e7f-8a90-1b2c3d4e5f60",
"spent_date": "2026-08-03",
"hours": 2,
"notes": "Homepage hero — second revision round",
"billable": true,
"is_locked": false,
"is_billed": false,
"is_running": false,
"approval_status": "unsubmitted",
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair"
},
"project": {
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand"
},
"task": {
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design"
},
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"external_reference": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}PATCH/v1/time_entries/{id}/stopStop the running timer on an entryA timer that has run past the sanity cap refuses with 409 and a message; re-send with actual_hours to bank the real figure.
PARAMETERSid (required) — The time entry id (UUID)actual_hours — Hours actually worked. Only needed to stop a timer that ran implausibly long and was refused (409).{
"id": "3f0a1c2e-5b6d-4e7f-8a90-1b2c3d4e5f60",
"spent_date": "2026-08-03",
"hours": 2,
"notes": "Homepage hero — second revision round",
"billable": true,
"is_locked": false,
"is_billed": false,
"is_running": false,
"approval_status": "unsubmitted",
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair"
},
"project": {
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand"
},
"task": {
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design"
},
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"external_reference": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}DELETE/v1/time_entries/{id}/external_referenceRemove an entry’s external referenceid (required) — The time entry id (UUID){
"message": "Project not found"
}Clients, their currencies and notes, and the people to contact there.
GET/v1/clientsList clientsArchived clients are left out unless include_archived=true.
PARAMETERSname — Case-insensitive name fragmentname_exact — The whole name, ignoring capitalization. Use this to look a record up before creating it.include_archived — true to include archived records, which are otherwise left outpage — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"clients": [
{
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp",
"is_active": true,
"address": "14 Bridge Street, Bristol BS1 4TW",
"currency": "USD",
"notes": "Invoices to accounts payable, net 30.",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/clients?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/clients?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/clientsCreate a client · manager+With find_existing, a match returns 200 and the client as it is: the other fields are not applied to it. No match creates one and returns 201.
BODYname — Requiredcurrency — 3-letter codeaddress — Postal addressnotes — Notesfind_existing — true to return the active client of this name (ignoring capitalization) instead of creating a second one{
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp",
"is_active": true,
"address": "14 Bridge Street, Bristol BS1 4TW",
"currency": "USD",
"notes": "Invoices to accounts payable, net 30.",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}GET/v1/clients/{id}Retrieve a clientid (required) — The client id (UUID){
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp",
"is_active": true,
"address": "14 Bridge Street, Bristol BS1 4TW",
"currency": "USD",
"notes": "Invoices to accounts payable, net 30.",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}PATCH/v1/clients/{id}Update a client · manager+id (required) — The client id (UUID)name — Client namecurrency — 3-letter codeaddress — Postal addressnotes — Notes{
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp",
"is_active": true,
"address": "14 Bridge Street, Bristol BS1 4TW",
"currency": "USD",
"notes": "Invoices to accounts payable, net 30.",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}DELETE/v1/clients/{id}Archive a client · manager+Archives the client; it is not deleted, and keeps its projects, invoices and contacts. Refuses (422) while the client has active projects. Deleting for good is done in the app, by an owner or admin, once the client is archived and has nothing attached.
PARAMETERSid (required) — The client id (UUID){
"message": "Project not found"
}GET/v1/clients/{id}/contactsList a client’s contactsSorted by name.
PARAMETERSid (required) — The client id (UUID)page — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"contacts": [
{
"id": "91a2b3c4-d5e6-4f70-8a9b-0c1d2e3f4a5b",
"name": "Dana Reyes",
"title": "Finance lead",
"email": "[email protected]",
"phone": "+1 555 0142",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e"
},
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/clients/ID/contacts?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/clients/ID/contacts?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/clients/{id}/contactsAdd a contact to a client · manager+Unknown fields are refused with 400 rather than ignored. To find-or-create a contact, POST and treat 409 as found: its body carries the existing contact’s id.
PARAMETERSid (required) — The client id (UUID)name — Requiredtitle — Job titleemail — Email address. Unique on the client, ignoring capitalization.phone — Phone number{
"id": "91a2b3c4-d5e6-4f70-8a9b-0c1d2e3f4a5b",
"name": "Dana Reyes",
"title": "Finance lead",
"email": "[email protected]",
"phone": "+1 555 0142",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e"
},
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}PATCH/v1/clients/{id}/contacts/{contactId}Update a contact · manager+Send only the fields to change; null clears title, email or phone.
PARAMETERSid (required) — The client id (UUID)contactId (required) — The contact id (UUID)name — Contact nametitle — Job titleemail — Email address. Unique on the client, ignoring capitalization.phone — Phone number{
"id": "91a2b3c4-d5e6-4f70-8a9b-0c1d2e3f4a5b",
"name": "Dana Reyes",
"title": "Finance lead",
"email": "[email protected]",
"phone": "+1 555 0142",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e"
},
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}DELETE/v1/clients/{id}/contacts/{contactId}Remove a contact · manager+id (required) — The client id (UUID)contactId (required) — The contact id (UUID){
"message": "Project not found"
}Projects, and who is assigned to them.
GET/v1/projectsList projectsArchived projects are left out unless include_archived=true or is_active=false. Combine client_id and name_exact to look a project up before creating it.
PARAMETERSclient_id — Filter by clientengagement_id — Filter by engagement. Add include_archived=true to see all of an engagement’s projectsname — Case-insensitive name fragmentname_exact — The whole name, ignoring capitalization. Use this to look a record up before creating it.is_active — true for active projects only, false for archived onlyinclude_archived — true to include archived records, which are otherwise left outpage — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"projects": [
{
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand",
"code": null,
"is_active": true,
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"hourly_rate": 145,
"engagement_id": "a2b3c4d5-e6f7-4081-9a2b-3c4d5e6f7a8b",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z",
"billing_type": "hourly_project_rate",
"fixed_fee": null,
"budget": 120,
"budget_is_monthly": false,
"over_budget_notification_percentage": 80,
"show_budget_to_all": false,
"cost_budget": 9000,
"cost_budget_include_expenses": false,
"fee_budget": 18000,
"fee_budget_is_monthly": false,
"fee_budget_notification_percentage": 80
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/projects?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/projects?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/projectsCreate a project · manager+With find_existing, a match returns 200 and the project as it is, budget and billing included: the budget fields, billing_type and fixed_fee apply only to a project this call creates. No match creates one and returns 201. A client_id from another workspace is 404. Unknown fields are refused with 400.
BODYclient_id — UUID, requiredname — Requiredcode — Short codehourly_rate — Project hourly ratestarts_on — YYYY-MM-DDends_on — YYYY-MM-DDengagement_id — UUID of one of the client’s engagements; null takes the project out of its engagementbilling_type — hourly_project_rate, hourly_person_rate, hourly_task_rate, fixed_fee or non_billablefixed_fee — The fee for a fixed_fee project, in its currency; null clears it. Refused (422) on any other billing typebudget — Project hours budget; null removes itbudget_is_monthly — true makes budget a monthly allowance instead of a totalover_budget_notification_percentage — When the over-budget alert fires, 1-100 (default 80); null switches alerts off. Left out, it keeps its value. Reads back as null when alerts are off, or when the project has no hours or cost budget (budget and cost_budget null)show_budget_to_all — true lets members see budget progresscost_budget — Internal cost budget in the workspace currency; null removes itcost_budget_include_expenses — true counts expenses against cost_budgetfee_budget — Fee budget in the project currency; null removes it. The billable hours at their hourly rates are measured against it, on a fixed-fee project toofee_budget_is_monthly — true makes fee_budget a monthly amount instead of a totalfee_budget_notification_percentage — When the fee budget alert fires, 1-100 (default 80); null switches it off. Separate from the hours budget thresholdfind_existing — true to return the client’s active project of this name (ignoring capitalization) instead of creating a second one{
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand",
"code": null,
"is_active": true,
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"hourly_rate": 145,
"engagement_id": "a2b3c4d5-e6f7-4081-9a2b-3c4d5e6f7a8b",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z",
"billing_type": "hourly_project_rate",
"fixed_fee": null,
"budget": 120,
"budget_is_monthly": false,
"over_budget_notification_percentage": 80,
"show_budget_to_all": false,
"cost_budget": 9000,
"cost_budget_include_expenses": false,
"fee_budget": 18000,
"fee_budget_is_monthly": false,
"fee_budget_notification_percentage": 80
}ERRORS{
"message": "Project not found"
}GET/v1/projects/{id}Retrieve a projectA plain member sees budget only when show_budget_to_all is true, and never sees cost_budget or the fee budget fields (null), whatever show_budget_to_all says. billing_type and fixed_fee are null for a plain member. On a fixed_fee project, reports count fixed_fee spread over the billable hours logged to the project across all time; changing billing_type away from fixed_fee keeps the fee, as the app does.
PARAMETERSid (required) — The project id (UUID){
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand",
"code": null,
"is_active": true,
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"hourly_rate": 145,
"engagement_id": "a2b3c4d5-e6f7-4081-9a2b-3c4d5e6f7a8b",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z",
"billing_type": "hourly_project_rate",
"fixed_fee": null,
"budget": 120,
"budget_is_monthly": false,
"over_budget_notification_percentage": 80,
"show_budget_to_all": false,
"cost_budget": 9000,
"cost_budget_include_expenses": false,
"fee_budget": 18000,
"fee_budget_is_monthly": false,
"fee_budget_notification_percentage": 80
}ERRORS{
"message": "Project not found"
}PATCH/v1/projects/{id}Update a project · manager+Moving a project to another client while it is in an engagement is refused (422) unless the same request sets engagement_id to null or to one of the new client’s engagements. Send only the fields to change: the budget fields left out keep their values. budget_by is not accepted, because a project can carry a project budget, per-person budgets and per-task budgets at once. Unknown fields are refused with 400.
PARAMETERSid (required) — The project id (UUID)client_id — UUIDname — Project namecode — Short codeis_active — false archives, true restoreshourly_rate — Project hourly ratestarts_on — YYYY-MM-DDends_on — YYYY-MM-DDengagement_id — UUID of one of the client’s engagements; null takes the project out of its engagementbilling_type — hourly_project_rate, hourly_person_rate, hourly_task_rate, fixed_fee or non_billablefixed_fee — The fee for a fixed_fee project, in its currency; null clears it. Refused (422) on any other billing typebudget — Project hours budget; null removes itbudget_is_monthly — true makes budget a monthly allowance instead of a totalover_budget_notification_percentage — When the over-budget alert fires, 1-100 (default 80); null switches alerts off. Left out, it keeps its value. Reads back as null when alerts are off, or when the project has no hours or cost budget (budget and cost_budget null)show_budget_to_all — true lets members see budget progresscost_budget — Internal cost budget in the workspace currency; null removes itcost_budget_include_expenses — true counts expenses against cost_budgetfee_budget — Fee budget in the project currency; null removes it. The billable hours at their hourly rates are measured against it, on a fixed-fee project toofee_budget_is_monthly — true makes fee_budget a monthly amount instead of a totalfee_budget_notification_percentage — When the fee budget alert fires, 1-100 (default 80); null switches it off. Separate from the hours budget threshold{
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand",
"code": null,
"is_active": true,
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"hourly_rate": 145,
"engagement_id": "a2b3c4d5-e6f7-4081-9a2b-3c4d5e6f7a8b",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z",
"billing_type": "hourly_project_rate",
"fixed_fee": null,
"budget": 120,
"budget_is_monthly": false,
"over_budget_notification_percentage": 80,
"show_budget_to_all": false,
"cost_budget": 9000,
"cost_budget_include_expenses": false,
"fee_budget": 18000,
"fee_budget_is_monthly": false,
"fee_budget_notification_percentage": 80
}ERRORS{
"message": "Project not found"
}DELETE/v1/projects/{id}Archive a project · manager+Archives the project; it is not deleted, and keeps its time, expenses and tasks. Deleting for good is done in the app, by an owner or admin, once the project is archived and has nothing attached.
PARAMETERSid (required) — The project id (UUID){
"message": "Project not found"
}GET/v1/projects/{id}/user_assignmentsList a project’s peopleid (required) — The project id (UUID)page — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"user_assignments": [
{
"id": "5e6f7081-92a3-4d4e-b5c6-d7e8f90a1b2c",
"is_project_manager": false,
"is_active": true,
"hourly_rate": 145,
"budget": 40,
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair"
},
"project": {
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand"
}
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/projects/ID/user_assignments?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/projects/ID/user_assignments?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/projects/{id}/user_assignmentsAssign a person to a project · manager+id (required) — The project id (UUID)user_id — Member UUID, requiredis_project_manager — Grants the PM flaghourly_rate — Per-assignment billable ratebudget — This person’s hours budget on the project, up to two decimals{
"id": "5e6f7081-92a3-4d4e-b5c6-d7e8f90a1b2c",
"is_project_manager": false,
"is_active": true,
"hourly_rate": 145,
"budget": 40,
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair"
},
"project": {
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand"
}
}ERRORS{
"message": "Project not found"
}PATCH/v1/projects/{id}/user_assignments/{assignmentId}Update an assignment · manager+The assignment must belong to the project in the path, or 404 and nothing changes. budget is null for a plain member reading assignments, as hourly_rate is.
PARAMETERSid (required) — The project id (UUID)assignmentId (required) — Assignment UUIDis_project_manager — PM flaghourly_rate — Per-assignment billable ratebudget — This person’s hours budget on the project; null removes it{
"id": "5e6f7081-92a3-4d4e-b5c6-d7e8f90a1b2c",
"is_project_manager": false,
"is_active": true,
"hourly_rate": 145,
"budget": 40,
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair"
},
"project": {
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand"
}
}ERRORS{
"message": "Project not found"
}DELETE/v1/projects/{id}/user_assignments/{assignmentId}Remove an assignment · manager+id (required) — The project id (UUID)assignmentId (required) — Assignment UUID{
"message": "Project not found"
}GET/v1/projects/{id}/task_assignmentsList a project’s tasks (compatibility view)Tasks belong to projects here, so each task is its own assignment.
PARAMETERSid (required) — The project id (UUID)page — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"tasks": [
{
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design",
"project_id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"billable_by_default": true,
"default_hourly_rate": 145,
"budget": 24,
"is_active": true,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/projects/ID/task_assignments?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/projects/ID/task_assignments?page=1"
}
}ERRORS{
"message": "Project not found"
}Named groups of one client’s projects — “Support”, “Website rebuild” — for reporting and budgeting across them. Two clients can each have an engagement of the same name.
GET/v1/engagementsList engagementsSorted by client, then name. project_count includes archived projects. List an engagement’s projects with GET /v1/projects?engagement_id=&include_archived=true.
PARAMETERSclient_id — One client’s engagementspage — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"engagements": [
{
"id": "a2b3c4d5-e6f7-4081-9a2b-3c4d5e6f7a8b",
"name": "Support",
"notes": "Annual support terms, renewed each January.",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"project_count": 3,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/engagements?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/engagements?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/engagementsCreate an engagement · manager+Adds no projects: set engagement_id on a project to put it in. Unknown fields are refused with 400.
BODYclient_id — UUID, requiredname — Required. Unique on the client, ignoring capitalizationnotes — Notes{
"id": "a2b3c4d5-e6f7-4081-9a2b-3c4d5e6f7a8b",
"name": "Support",
"notes": "Annual support terms, renewed each January.",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"project_count": 3,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}GET/v1/engagements/{id}Retrieve an engagementid (required) — The engagement id (UUID){
"id": "a2b3c4d5-e6f7-4081-9a2b-3c4d5e6f7a8b",
"name": "Support",
"notes": "Annual support terms, renewed each January.",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"project_count": 3,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}PATCH/v1/engagements/{id}Update an engagement · manager+The client cannot be changed: the engagement’s projects belong to it. Unknown fields are refused with 400.
PARAMETERSid (required) — The engagement id (UUID)name — Engagement namenotes — Notes; null clears them{
"id": "a2b3c4d5-e6f7-4081-9a2b-3c4d5e6f7a8b",
"name": "Support",
"notes": "Annual support terms, renewed each January.",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"project_count": 3,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}DELETE/v1/engagements/{id}Delete an engagement · manager+Deletes the engagement for good. Its projects are kept, archived ones included, and simply have no engagement afterwards.
PARAMETERSid (required) — The engagement id (UUID){
"message": "Project not found"
}Tasks (each belongs to one project).
GET/v1/tasksList tasksproject_id — Filter by projectpage — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"tasks": [
{
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design",
"project_id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"billable_by_default": true,
"default_hourly_rate": 145,
"budget": 24,
"is_active": true,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/tasks?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/tasks?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/tasksCreate a task · manager+project_id — UUID, requiredname — Requiredbillable_by_default — Defaults truedefault_hourly_rate — Task rate{
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design",
"project_id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"billable_by_default": true,
"default_hourly_rate": 145,
"budget": 24,
"is_active": true,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}GET/v1/tasks/{id}Retrieve a taskid (required) — The task id (UUID){
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design",
"project_id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"billable_by_default": true,
"default_hourly_rate": 145,
"budget": 24,
"is_active": true,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}PATCH/v1/tasks/{id}Update a task · manager+id (required) — The task id (UUID)name — Task namebillable_by_default — true/falsedefault_hourly_rate — Task ratebudget — Task hours budget, up to two decimals; null removes it{
"id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Design",
"project_id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"billable_by_default": true,
"default_hourly_rate": 145,
"budget": 24,
"is_active": true,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}DELETE/v1/tasks/{id}Delete a task · manager+id (required) — The task id (UUID){
"message": "Project not found"
}Employee profiles from the People add-on: job title, dates, contact details, emergency contact, linked accounts and recorded systems access. Personal access tokens created with **Include People profile data** only (the `people` scope) — it is never on by default, an organization key is refused, and connected (OAuth) apps cannot reach these. A token belonging to an HR admin sees everybody, including archived people; any other token sees only its owner. Compensation and documents are never returned.
GET/v1/peopleList profiles · personal token`systems_with_access` is null when the workspace does not track systems access or the token is not an HR admin’s — null never means "none".
PARAMETERSinclude_archived — false leaves out people who have left. Defaults to true.page — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"people": [
{
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair",
"email": "[email protected]",
"is_archived": false,
"is_invited": false,
"has_profile": true,
"percent_complete": 90,
"job_title": "Senior Designer",
"start_date": "2024-03-04",
"end_date": null,
"personal_email": "[email protected]",
"phone": "+1 555 0100",
"address": {
"line_1": "12 Example Street",
"line_2": null,
"city": "Austin",
"region": "TX",
"postcode": "78701",
"country": "US"
},
"emergency_contact": {
"name": "Arun Nair",
"phone": "+1 555 0101",
"relation": "Brother"
},
"accounts": {
"github": "priya-nair",
"slack": null,
"jira": null,
"linkedin": null
},
"systems_with_access": [
"Google Workspace",
"GitHub"
]
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/people?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/people?page=1"
}
}ERRORS{
"message": "Project not found"
}GET/v1/people/{id}Retrieve a profile · personal tokenReturns 404 for a person the token may not see, the same as for one who does not exist.
PARAMETERSid (required) — The membership id (UUID){
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair",
"email": "[email protected]",
"is_archived": false,
"is_invited": false,
"has_profile": true,
"percent_complete": 90,
"job_title": "Senior Designer",
"start_date": "2024-03-04",
"end_date": null,
"personal_email": "[email protected]",
"phone": "+1 555 0100",
"address": {
"line_1": "12 Example Street",
"line_2": null,
"city": "Austin",
"region": "TX",
"postcode": "78701",
"country": "US"
},
"emergency_contact": {
"name": "Arun Nair",
"phone": "+1 555 0101",
"relation": "Brother"
},
"accounts": {
"github": "priya-nair",
"slack": null,
"jira": null,
"linkedin": null
},
"systems_with_access": [
"Google Workspace",
"GitHub"
]
}ERRORS{
"message": "Project not found"
}Workspace members. Rates are money — manager+ tokens only.
GET/v1/usersList usersrole — owner, admin, manager, or memberpage — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"users": [
{
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair",
"first_name": "Priya",
"last_name": "Nair",
"email": "[email protected]",
"kind": "human",
"is_active": true,
"access_roles": [
"member"
],
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/users?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/users?page=1"
}
}ERRORS{
"message": "Project not found"
}GET/v1/users/meRetrieve the token’s owner · personal token{
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair",
"first_name": "Priya",
"last_name": "Nair",
"email": "[email protected]",
"kind": "human",
"is_active": true,
"access_roles": [
"member"
],
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}GET/v1/users/{id}Retrieve a userid (required) — The user id (UUID){
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair",
"first_name": "Priya",
"last_name": "Nair",
"email": "[email protected]",
"kind": "human",
"is_active": true,
"access_roles": [
"member"
],
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}GET/v1/users/{id}/billable_ratesList a user’s billable rates · manager+id (required) — The user id (UUID)page — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"billable_rates": [
{
"id": "6f708192-a3b4-4e5f-c6d7-e8f90a1b2c3d",
"amount": 145,
"start_date": "2026-01-01",
"end_date": null
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/users/ID/billable_rates?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/users/ID/billable_rates?page=1"
}
}ERRORS{
"message": "Project not found"
}GET/v1/users/{id}/cost_ratesList a user’s cost rates · manager+id (required) — The user id (UUID)page — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"cost_rates": [
{
"id": "6f708192-a3b4-4e5f-c6d7-e8f90a1b2c3d",
"amount": 68,
"start_date": "2026-01-01",
"end_date": null
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/users/ID/cost_rates?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/users/ID/cost_rates?page=1"
}
}ERRORS{
"message": "Project not found"
}GET/v1/users/{id}/project_assignmentsList a user’s project assignmentsid (required) — The user id (UUID)page — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"user_assignments": [
{
"id": "5e6f7081-92a3-4d4e-b5c6-d7e8f90a1b2c",
"is_project_manager": false,
"is_active": true,
"hourly_rate": 145,
"budget": 40,
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair"
},
"project": {
"id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"name": "Acme Rebrand"
}
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/users/ID/project_assignments?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/users/ID/project_assignments?page=1"
}
}ERRORS{
"message": "Project not found"
}GET/v1/users/{id}/teammatesList a user’s direct reportsid (required) — The user id (UUID)page — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"users": [
{
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"name": "Priya Nair",
"first_name": "Priya",
"last_name": "Nair",
"email": "[email protected]",
"kind": "human",
"is_active": true,
"access_roles": [
"member"
],
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/users/ID/teammates?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/users/ID/teammates?page=1"
}
}ERRORS{
"message": "Project not found"
}Work roles (job functions), assignable to members.
GET/v1/rolesList rolespage — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"roles": [
{
"id": "80192a3b-4c5d-4f60-9718-293a4b5c6d7e",
"name": "Senior designer",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/roles?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/roles?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/rolesCreate a role · manager+name — Required{
"id": "80192a3b-4c5d-4f60-9718-293a4b5c6d7e",
"name": "Senior designer",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}PATCH/v1/roles/{id}Update a role · manager+id (required) — The role id (UUID)name — Required{
"id": "80192a3b-4c5d-4f60-9718-293a4b5c6d7e",
"name": "Senior designer",
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}DELETE/v1/roles/{id}Delete a role · manager+id (required) — The role id (UUID){
"message": "Project not found"
}Invoices, payments, and sending. Admin tokens or organization keys only.
GET/v1/invoicesList invoicesstate — draft, open, partially_paid, paid, closedclient_id — Filter by clientpage — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"invoices": [
{
"id": "4d5e6f70-8192-4c3d-a4b5-c6d7e8f90a1b",
"number": "1042",
"state": "draft",
"subject": "August retainer",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"amount": 5400,
"paid_amount": 0,
"due_amount": 5400,
"currency": "USD",
"issue_date": "2026-08-01",
"due_date": "2026-08-31",
"period_start": "2026-07-01",
"period_end": "2026-07-31",
"sent_at": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/invoices?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/invoices?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/invoicesCreate an invoice · adminProvide line_items OR period_start+period_end. Generated invoices mark hours billed so nothing is double-invoiced.
BODYclient_id — UUID, requiredline_items — Array of {kind, description, quantity, unit_price} for a free-form invoiceperiod_start — With period_end: generate from uninvoiced time insteadperiod_end — YYYY-MM-DDgrouping — task (default), person, or detailedinclude_expenses — Defaults truesubject — Subject lineissue_date — Defaults todaydue_date — YYYY-MM-DDtax — Percentdiscount — Percent{
"id": "4d5e6f70-8192-4c3d-a4b5-c6d7e8f90a1b",
"number": "1042",
"state": "draft",
"subject": "August retainer",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"amount": 5400,
"paid_amount": 0,
"due_amount": 5400,
"currency": "USD",
"issue_date": "2026-08-01",
"due_date": "2026-08-31",
"period_start": "2026-07-01",
"period_end": "2026-07-31",
"sent_at": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}GET/v1/invoices/{id}Retrieve an invoice (lines + payments)id (required) — The invoice id (UUID){
"id": "4d5e6f70-8192-4c3d-a4b5-c6d7e8f90a1b",
"number": "1042",
"state": "draft",
"subject": "August retainer",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"amount": 5400,
"paid_amount": 0,
"due_amount": 5400,
"currency": "USD",
"issue_date": "2026-08-01",
"due_date": "2026-08-31",
"period_start": "2026-07-01",
"period_end": "2026-07-31",
"sent_at": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}GET/v1/invoices/{id}/ublDownload the invoice as a UBL 2.1 (EN 16931) XML e-invoiceReturns application/xml (not the JSON envelope). Supplier details come from Invoices → Configure → Company info.
PARAMETERSid (required) — The invoice id (UUID){
"message": "Project not found"
}PATCH/v1/invoices/{id}Update a draft invoice · adminid (required) — The invoice id (UUID)subject — Subjectnotes — Notesissue_date — YYYY-MM-DDdue_date — YYYY-MM-DDtax — Percentdiscount — Percent{
"id": "4d5e6f70-8192-4c3d-a4b5-c6d7e8f90a1b",
"number": "1042",
"state": "draft",
"subject": "August retainer",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"amount": 5400,
"paid_amount": 0,
"due_amount": 5400,
"currency": "USD",
"issue_date": "2026-08-01",
"due_date": "2026-08-31",
"period_start": "2026-07-01",
"period_end": "2026-07-31",
"sent_at": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}DELETE/v1/invoices/{id}Delete a draft invoice · adminid (required) — The invoice id (UUID){
"message": "Project not found"
}POST/v1/invoices/{id}/messagesMark an invoice sent · adminMoves draft → open. Email delivery stays a deliberate in-app action.
PARAMETERSid (required) — The invoice id (UUID){
"id": "4d5e6f70-8192-4c3d-a4b5-c6d7e8f90a1b",
"event_type": "send",
"state": "open"
}ERRORS{
"message": "Project not found"
}POST/v1/invoices/{id}/paymentsRecord a payment · adminid (required) — The invoice id (UUID)amount — Requiredpaid_date — YYYY-MM-DD, requirednotes — Notes{
"id": "4d5e6f70-8192-4c3d-a4b5-c6d7e8f90a1b",
"number": "1042",
"state": "draft",
"subject": "August retainer",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"amount": 5400,
"paid_amount": 0,
"due_amount": 5400,
"currency": "USD",
"issue_date": "2026-08-01",
"due_date": "2026-08-31",
"period_start": "2026-07-01",
"period_end": "2026-07-31",
"sent_at": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}DELETE/v1/invoices/{id}/payments/{paymentId}Delete a payment · adminid (required) — The invoice id (UUID)paymentId (required) — Payment UUID{
"message": "Project not found"
}Quotes that convert to invoices. Admin tokens or organization keys only.
GET/v1/estimatesList estimates · manager+page — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"estimates": [
{
"id": "b2c3d4e5-f607-4182-93a4-b5c6d7e8f90a",
"number": "204",
"state": "draft",
"subject": "Rebrand — phase two",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"amount": 18500,
"currency": "USD",
"issue_date": "2026-08-01",
"created_at": "2026-08-03T09:12:00.000Z"
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/estimates?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/estimates?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/estimatesCreate an estimate · adminclient_id — UUID, requiredline_items — Array of {kind, description, quantity, unit_price}, requiredsubject — Subjectissue_date — Defaults todaytax — Percentdiscount — Percent{
"id": "b2c3d4e5-f607-4182-93a4-b5c6d7e8f90a",
"number": "204",
"state": "draft",
"subject": "Rebrand — phase two",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"amount": 18500,
"currency": "USD",
"issue_date": "2026-08-01",
"created_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}GET/v1/estimates/{id}Retrieve an estimate · manager+id (required) — The estimate id (UUID){
"id": "b2c3d4e5-f607-4182-93a4-b5c6d7e8f90a",
"number": "204",
"state": "draft",
"subject": "Rebrand — phase two",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"amount": 18500,
"currency": "USD",
"issue_date": "2026-08-01",
"created_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}PATCH/v1/estimates/{id}Change estimate state · adminid (required) — The estimate id (UUID)state — sent, accepted, or declined{
"id": "b2c3d4e5-f607-4182-93a4-b5c6d7e8f90a",
"number": "204",
"state": "draft",
"subject": "Rebrand — phase two",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"amount": 18500,
"currency": "USD",
"issue_date": "2026-08-01",
"created_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}DELETE/v1/estimates/{id}Delete a draft estimate · adminid (required) — The estimate id (UUID){
"message": "Project not found"
}POST/v1/estimates/{id}/convertConvert to a draft invoice · adminid (required) — The estimate id (UUID){
"id": "4d5e6f70-8192-4c3d-a4b5-c6d7e8f90a1b",
"number": "1042",
"state": "draft",
"subject": "August retainer",
"client": {
"id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"name": "Acme Corp"
},
"amount": 5400,
"paid_amount": 0,
"due_amount": 5400,
"currency": "USD",
"issue_date": "2026-08-01",
"due_date": "2026-08-31",
"period_start": "2026-07-01",
"period_end": "2026-07-31",
"sent_at": null,
"created_at": "2026-08-03T09:12:00.000Z",
"updated_at": "2026-08-03T09:12:00.000Z"
}ERRORS{
"message": "Project not found"
}Expenses and categories. Member tokens manage their own.
GET/v1/expensesList expensesuser_id — Required with an organization keyfrom — YYYY-MM-DDto — YYYY-MM-DDpage — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"expenses": [
{
"id": "90a1b2c3-d4e5-4f60-8172-93a4b5c6d7e8",
"spent_date": "2026-08-03",
"total_cost": 42.5,
"billable": true,
"is_reimbursable": true,
"notes": "Stock photography license",
"project": {
"name": "Acme Rebrand"
},
"client": {
"name": "Acme Corp"
},
"expense_category": {
"name": "Software"
},
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9"
}
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/expenses?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/expenses?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/expensesCreate an expensespent_date — YYYY-MM-DD, requiredtotal_cost — Requiredproject_id — UUIDexpense_category_id — UUIDbillable — true/falseis_reimbursable — true/falsenotes — Notesuser_id — Required with an organization key{
"id": "90a1b2c3-d4e5-4f60-8172-93a4b5c6d7e8",
"spent_date": "2026-08-03",
"total_cost": 42.5,
"billable": true,
"is_reimbursable": true,
"notes": "Stock photography license",
"project": {
"name": "Acme Rebrand"
},
"client": {
"name": "Acme Corp"
},
"expense_category": {
"name": "Software"
},
"user": {
"id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9"
}
}ERRORS{
"message": "Project not found"
}DELETE/v1/expenses/{id}Delete an expenseid (required) — The expense id (UUID){
"message": "Project not found"
}GET/v1/expense_categoriesList expense categoriespage — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"expense_categories": [
{
"id": "a1b2c3d4-e5f6-4071-8293-a4b5c6d7e8f9",
"name": "Software",
"unit_price": null,
"is_active": true
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/expense_categories?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/expense_categories?page=1"
}
}ERRORS{
"message": "Project not found"
}POST/v1/expense_categoriesCreate an expense category · manager+name — Requiredunit_price — For unit-based categories{
"id": "a1b2c3d4-e5f6-4071-8293-a4b5c6d7e8f9",
"name": "Software",
"unit_price": null,
"is_active": true
}ERRORS{
"message": "Project not found"
}Aggregations with money — manager+ tokens or organization keys.
GET/v1/reports/time/{dimension}Time report by clients, projects, tasks, or team · manager+`billable_amount` is billable hours at their hourly rates, except on a fixed-fee project, where it is the fee spread over all the billable hours logged to the project: a range gets its share of the fee, and the figure moves as more hours are logged. A fixed-fee project with no fee set earns 0, and its row says `fixed_fee_not_set: true`. Uninvoiced amounts and invoices still price time at hourly rates. Rows are grouped by id, so two projects (or clients, or people) with the same name are two rows. Each carries its id beside its name — `client_id` on clients rows, `project_id` with `client_id` and `client_name` on projects rows, `user_id` on team rows — and a name that another one in the workspace shares is qualified: a project with its client, "Support (Northwind)", and numbered as well if another such project has a client of the same name, "Support (Northwind) (2)"; a client or person with a number, "Alex Kim (2)". Match on the ids, not the names. Rows are paginated like every list: `per_page` rows a page (100 by default, up to 2,000), with `total_entries` counting them all — follow `next_page`. Tasks group by name across projects, as one kind of work.
PARAMETERSdimension (required) — clients, projects, tasks, team, or engagementsfrom (required) — YYYY-MM-DDto (required) — YYYY-MM-DD{
"project_name": "Acme Rebrand",
"project_id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"client_id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"client_name": "Acme Corp",
"total_hours": 128.5,
"billable_hours": 119,
"billable_amount": 17255,
"fixed_fee_not_set": false
}ERRORS{
"message": "Project not found"
}GET/v1/reports/expenses/{dimension}Expense report by categories, projects, or team · manager+dimension (required) — categories, projects, or teamfrom (required) — YYYY-MM-DDto (required) — YYYY-MM-DD{
"project": "Acme Rebrand",
"total_amount": 412.75,
"billable_amount": 380
}ERRORS{
"message": "Project not found"
}GET/v1/reports/project_budgetHours budget vs actual per project · manager+One row per project with an hours, cost or fee budget, or with per-person budgets; client_id and client_name say whose project it is. When budget_resets_monthly is true, budget is ONE month’s allowance and budget_remaining subtracts the whole range’s spend from it; compare budget_spent with budget_for_range instead, which is budget times months_in_range (the calendar months the range touches), with budget_remaining_for_range beside it. For a budget that does not reset, budget_for_range equals budget. A project budgeted only per person has budget, budget_for_range and both remaining fields null. budget_by says what budget is: project_hours whenever the project has an hours budget, whatever else it carries. With none, budget is null and budget_by names the budget the row is listed for instead: project_fees (see the fee_budget fields), then person_hours (see budget_people), then internal_cost (see cost_budget), in that order when there are several; none is a row with no budget set. budget_people is empty unless the project has per-person budgets; when it has, it lists each person with a budget and everyone else who logged time in the range, assigned or not, with budget and budget_remaining null and has_budget false. Where the project has a fee budget, the row adds fee_budget, fee_budget_is_monthly, fee_budget_spent (the billable hours in range at their hourly rates — not the fixed-fee allocation), fee_budget_remaining, and fee_budget_for_range with fee_budget_remaining_for_range, scaled by months_in_range like the hours; without one these fields are absent.
PARAMETERSfrom (required) — YYYY-MM-DDto (required) — YYYY-MM-DD{
"results": [
{
"project_id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"project_name": "Acme Rebrand",
"client_id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"client_name": "Acme Corp",
"budget": 40,
"budget_spent": 98,
"budget_remaining": -58,
"budget_by": "project_hours",
"budget_resets_monthly": true,
"months_in_range": 3,
"budget_for_range": 120,
"budget_remaining_for_range": 22,
"fee_budget": 6000,
"fee_budget_is_monthly": true,
"fee_budget_spent": 14250,
"fee_budget_remaining": -8250,
"fee_budget_for_range": 18000,
"fee_budget_remaining_for_range": 3750,
"budget_people": [
{
"user_id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"user_name": "Ana Ortiz",
"budget": 30,
"budget_spent": 26.5,
"budget_remaining": 3.5,
"has_budget": true
}
]
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/reports/project_budget?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/reports/project_budget?page=1"
}
}ERRORS{
"message": "Project not found"
}GET/v1/reports/engagement_budgetHours budget vs actual per engagement · manager+One row per engagement, archived projects included. budget_for_range sums its projects’ hours budgets over the range (a monthly budget times months_in_range), and is null when no project in it has one. actual_hours is every project in the engagement; budgeted_actual_hours only the projects with a budget, and budget_remaining_for_range is budget_for_range minus that — hours on an unbudgeted project are not set against another project’s budget. projects lists each project with its own figures. budget_people sums per-person budgets across the engagement’s projects that budget per person, and lists everyone else who logged time on them with budget null and has_budget false, as project_budget does. fee_budgets is the fee budgets of its projects over the range, one entry per currency (amounts in different currencies are never added): budget_for_range (a monthly fee budget times months_in_range), spent (the billable hours in the range at their hourly rates) and remaining_for_range; empty when no project has one. A project with a fee budget also carries fee_budget_for_range, fee_spent and currency.
PARAMETERSfrom (required) — YYYY-MM-DDto (required) — YYYY-MM-DDengagement_id — One engagement{
"results": [
{
"engagement_id": "a2b3c4d5-e6f7-4081-9a2b-3c4d5e6f7a8b",
"engagement_name": "Support",
"client_id": "2b3c4d5e-6f70-4b8c-9d0e-1f2a3b4c5d6e",
"client_name": "Acme Corp",
"months_in_range": 3,
"budget_for_range": 60,
"actual_hours": 52.5,
"budgeted_actual_hours": 48,
"budget_remaining_for_range": 12,
"projects": [
{
"project_id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"project_name": "Support 2026",
"is_active": true,
"budget_for_range": 60,
"actual_hours": 48,
"fee_budget_for_range": 9000,
"fee_spent": 6960,
"currency": "USD"
}
],
"fee_budgets": [
{
"currency": "USD",
"budget_for_range": 9000,
"spent": 6960,
"remaining_for_range": 2040
}
],
"budget_people": [
{
"user_id": "7a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"user_name": "Ana Ortiz",
"budget": 30,
"budget_spent": 26.5,
"budget_remaining": 3.5,
"has_budget": true
}
]
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/reports/engagement_budget?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/reports/engagement_budget?page=1"
}
}ERRORS{
"message": "Project not found"
}GET/v1/reports/uninvoicedApproved, unbilled hours and amounts per project · manager+page — Page number, starting at 1per_page — Items per page (default 100, max 2000){
"results": [
{
"project_id": "9c8b7a65-4321-4fed-9876-543210fedcba",
"project_name": "Acme Rebrand",
"client_name": "Acme Corp",
"uninvoiced_hours": 22.75,
"uninvoiced_amount": 3298.75,
"uninvoiced_expenses": 42.5
}
],
"per_page": 100,
"total_pages": 1,
"total_entries": 1,
"next_page": null,
"previous_page": null,
"page": 1,
"links": {
"first": "https://timetrakkr.com/api/v1/reports/uninvoiced?page=1",
"next": null,
"previous": null,
"last": "https://timetrakkr.com/api/v1/reports/uninvoiced?page=1"
}
}ERRORS{
"message": "Project not found"
}