Resources and endpoints
Time entries, clients, projects, tasks, tags, members, and reports: their methods, paths, and example payloads.
The API exposes the resources you already work with in Cadence. Every path below is relative to /api/v1, every request needs a bearer key with the right scope, and every result is confined to your bound organisation.
Time entries
Time entries are the core resource. List and read them with time:read; create and update them with time:write.
user_id, client, project_id, task_id, billable (true/false), and a from/to window on started_at.201.404 if it is not in your organisation.A create request looks like this:
POST /api/v1/time-entries HTTP/1.1
Authorization: Bearer ck_live_a1b2c3d4_...
Content-Type: application/json
Idempotency-Key: 9d1f6c2e-3b4a-4f7e-8c21-77a0b1c2d3e4{
"started_at": "2026-06-17T09:00:00Z",
"ended_at": "2026-06-17T10:30:00Z",
"client": "Acme Corp",
"project_id": "1f2e3d4c-5b6a-4789-90ab-cdef01234567",
"description": "Discovery call",
"billable": true
}The response wraps the created entry under data:
{
"data": {
"id": "0a9b8c7d-6e5f-4a3b-2c1d-0e9f8a7b6c5d",
"org_id": "abcdef01-2345-6789-abcd-ef0123456789",
"user_id": "11112222-3333-4444-5555-666677778888",
"started_at": "2026-06-17T09:00:00Z",
"ended_at": "2026-06-17T10:30:00Z",
"duration_seconds": 5400,
"client": "Acme Corp",
"client_id": null,
"project_id": "1f2e3d4c-5b6a-4789-90ab-cdef01234567",
"project": "Website rebuild",
"task_id": null,
"description": "Discovery call",
"billable": true,
"clickup_task_id": null,
"clickup_time_entry_id": null,
"source": "api",
"created_at": "2026-06-17T10:30:01Z"
}
}ended_at must not precede started_at; a reversed pair is a 422 validation error. duration_seconds is computed for you.
Idempotent creates
Send an Idempotency-Key header on a POST /time-entries to make retries safe. Replaying the same key with the same payload returns the original entry (200) instead of creating a duplicate; reusing the key with a different payload is rejected with 409 idempotency_conflict.
clickup_time_entry_id (and clickup_task_id) linking it to a pushed ClickUp entry. The API does not set or touch these fields: the desktop app's sync owns them. If you build a two-way sync, never write a null over an existing clickup_time_entry_id: clearing it makes Cadence re-push the entry and create a duplicate on the next sync. Treat that id as read-only and preserve it on every update.Catalogue: clients, projects, tasks, tags
The catalogue is the shared structure your time is tracked against. List with catalogue:read; create with catalogue:write (and an admin member, so creating catalogue records is an admin action. All four list endpoints accept status as a filter; projects also accept client_id, and tasks accept project_id.
client_id is required on create.project_id is required on create.Creating a client:
POST /api/v1/clients HTTP/1.1
Authorization: Bearer ck_live_a1b2c3d4_...
Content-Type: application/json{ "name": "Acme Corp", "code": "ACME", "colour": "#4f7cff" }{
"data": {
"id": "2b3c4d5e-6f70-4812-93a4-b5c6d7e8f901",
"org_id": "abcdef01-2345-6789-abcd-ef0123456789",
"name": "Acme Corp",
"code": "ACME",
"colour": "#4f7cff",
"status": "active",
"is_system": false,
"created_at": "2026-06-17T10:31:00Z",
"updated_at": "2026-06-17T10:31:00Z"
}
}Members
GET /members returns the organisation's members and roles. It needs members:read and an admin member: ordinary members get 403 forbidden_role. There is no member management over the API in v1; that stays in the product. See managing members.
{
"data": [
{
"user_id": "11112222-3333-4444-5555-666677778888",
"full_name": "Sam Carter",
"email": "sam@acme.example",
"org_role": "org_owner",
"status": "active",
"joined_at": "2026-05-01T08:00:00Z"
}
]
}Reports
The report endpoints return the same server-aggregated figures the in-app reports use, reconciled across views. They need reports:read and an admin member. All three require a from and to window, and accept tz_offset (defaulting to +00:00) plus repeatable client_id, user_id, project_id filters and a billable flag.
GET /api/v1/reports/by-client?from=2026-06-01T00:00:00Z&to=2026-07-01T00:00:00Z&tz_offset=%2B02:00 HTTP/1.1
Authorization: Bearer ck_live_a1b2c3d4_...Integrations
GET /integrations (integrations:read) returns connector status only: provider, a derived status of connected, expired, or disconnected, and timestamps. Connector tokens are never returned by the API.
Pagination
Larger collections (time entries) use keyset pagination, never offsets. Pass limit (1 to 500, default 100) and follow the opaque next_cursor from each page back in as ?cursor= until has_more is false:
{
"data": [ /* ... entries ... */ ],
"has_more": true,
"next_cursor": "eyJzdGFydGVkX2F0IjoiMjAyNi0wNi0xN1QwOTowMDowMFoiLCJpZCI6Ii4uLiJ9"
}Treat the cursor as opaque: do not parse or construct it. When has_more is false and next_cursor is null, you have reached the end of the list.