Skip to content

Using the API

The Kombine apps all talk to one REST API. With a personal access token your own tools can use it too. Everything here works with curl and jq.

  • Base URL: https://api.kombine.dev/v1
  • Auth: send the token as Authorization: Bearer kmb_pat_…
  • Format: JSON in, JSON out. Times are ISO 8601 (2026-10-23T09:00:00Z).
  • Full reference: the OpenAPI spec lists every endpoint and field. Paste it into any OpenAPI viewer, or hand it to your AI agent.

Set up your shell once:

Terminal window
export KOMBINE_TOKEN='kmb_pat_…'
export API='https://api.kombine.dev/v1'
k() { curl -sS -H "Authorization: Bearer $KOMBINE_TOKEN" -H 'Content-Type: application/json' "$@"; }

The examples below use that k helper.

Needs tasks: Read to list, Read & write to change.

List tasks. Results come in pages under data, with total, limit (default 200, max 500) and offset:

Terminal window
k "$API/tasks?limit=500" | jq '.data[] | {title, status, priority, due_date}'

There’s no server-side filter for status or date, so filter with jq. Open tasks due today or earlier:

Terminal window
today=$(date +%F)
k "$API/tasks?limit=500" | jq -r --arg d "$today" '
.data[] | select(.status != "DONE" and .is_archived == false and .due_date != null and .due_date[0:10] <= $d)
| "\(.priority // "-")\t\(.title)"'

Add a task. Your tool picks the task’s id (any UUID), the same way the apps do. That makes retries safe: sending the same id twice can’t create two tasks.

Terminal window
k -X POST "$API/tasks" -d "$(jq -n --arg id "$(uuidgen | tr A-Z a-z)" '{
id: $id,
title: "Renew passport",
priority: "HIGH",
due_date: "2026-11-01T00:00:00Z",
due_date_has_time: false,
tags: ["errands"]
}')"
  • priority: LOW, MEDIUM or HIGH (leave it out for none)
  • status: TODO (default), IN_PROGRESS or DONE
  • due_date_has_time: false means “due that day”, with no time

Change a task. PUT changes only the fields you send. Mark one done:

Terminal window
k -X PUT "$API/tasks/$TASK_ID" -d '{"status":"DONE"}'

Completing a task over the API counts the same as in the apps: it’s logged for your stats, and a repeating task moves to its next date.

Needs sessions: Read & write. Log a session you did somewhere else (a meeting, a run, time tracked in another app):

Terminal window
k -X POST "$API/sessions" -d "$(jq -n --arg id "$(uuidgen | tr A-Z a-z)" '{
id: $id,
mode: "POMODORO",
started_at: "2026-10-23T07:00:00Z",
ended_at: "2026-10-23T07:25:00Z",
duration_ms: 1500000,
note: "Pairing on the importer"
}')"

mode is POMODORO, FLOW or ADAPTIVE. Add task_id or activity_id to tag it. List your activities with k "$API/activities" | jq '.data[] | {id, name}'.

Needs timer: Read. See what’s running on any of your devices:

Terminal window
k "$API/timer/state" | jq '{status, mode, is_break, started_at, elapsed_ms, task_id}'

An empty response (204) means no timer is running.

Starting or stopping the timer from a script isn’t recommended. A session is only recorded by the app that ran the timer, so a timer started over the API leaves no session behind. To record time, log a session instead (see Focus sessions above).

Needs stats: Read.

Terminal window
k "$API/stats/all-time" | jq '{totalFocusSessions, hours: ((.totalFocusSeconds // 0) / 3600 | floor), longestStreakLength}'
k "$API/daily-snapshots?from=2026-10-01&to=2026-10-31" | jq '.snapshots[] | {date, sessions_completed, achieved}'

All-time stats are worked out in the background. On a brand-new account, stats/all-time answers 404 (“No stats available yet”) until they’ve been calculated for the first time.

Errors are JSON with an error_code you can match on, for example:

{ "title": "Forbidden", "status": 403, "detail": "Personal access token does not permit this route", "error_code": 1014 }
Status error_code Meaning What to do
400 5001, 5002 A field is missing or invalid; errors says which Fix the request
401 1013 Token expired Create a new token
401 1011 Token revoked Create a new token
401 1004 Token not recognised Check you copied it whole
403 1014 The token’s permissions don’t cover this Create a token with the right permissions
409 4003 That id is already taken Safe to ignore on a retry
409 4010 That external_ref is already used See Workflows
429 6001 Too many requests Wait Retry-After seconds

GET /v1/error-codes lists them all (no token needed).

Each token gets 60 requests a minute, with short bursts of up to 20. Past that you get 429 with a Retry-After header. Other tokens and your apps aren’t affected.