API

Track time, log finished work, and read what's unbilled — from scripts and AI agents, over REST or MCP. Built agent-first: JSON everywhere, idempotent operations, scoped tokens.

Machine-readable versions: openapi.json (full spec) and llms.txt (agent summary). Point your agent at either and it can figure the rest out itself.

Authentication

Create a personal access token under Settings → API tokens. Tokens are scoped — timer starts/stops/reads the timer, projects:read lists projects, entries:write logs and updates time entries, and reports:read reads unbilled totals. Send it as a Bearer header:

curl -H "Authorization: Bearer $TOKEN" https://tallymeter.com/api/v1/timer

Endpoints

GET /api/v1/timer scope: timer

Current timer state.

{"running": true, "entry": {"id": 42, "description": "ABC-1 fix login", "project_id": 1, "project_name": "Acme", "started_at": "2026-07-06T20:37:40+00:00", "ended_at": null, "duration_seconds": 918, "is_running": true, "source": "mcp", "agent": "claude-code"}}
POST /api/v1/timer/start scope: timer

Start a timer. Both fields are optional; any running timer is stopped first — only one timer runs at a time. Returns 201 with the new entry.

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"description": "ABC-1 fix login", "project_id": 1}' \
  https://tallymeter.com/api/v1/timer/start
POST /api/v1/timer/stop scope: timer

Stop the running timer; returns the stopped entry. Idempotent: if nothing is running you still get 200 with {"running": false, "stopped": null} — retries are always safe.

curl -X POST -H "Authorization: Bearer $TOKEN" https://tallymeter.com/api/v1/timer/stop
GET /api/v1/projects scope: projects:read

Your active projects — use their ids as project_id when starting a timer.

{"projects": [{"id": 1, "name": "Acme", "color": "#10b981"}]}
PATCH /api/v1/entries/{entry} scope: entries:write

Update an entry's description and/or project in place — running or finished — without touching its times. Made for the "timer started, described later" flow: no stopping, no splitting. Entries already billed on an invoice are frozen and return 409.

curl -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"description": "ABC-1 fix login"}' \
  https://tallymeter.com/api/v1/entries/42
POST /api/v1/feedback scope: any token

Feedback channel for agents — a capability you looked for and couldn't find, a bug, confusing docs. No specific scope required. Optional category (bug, missing-capability, docs, other) and tool it relates to. Every report is read.

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"message": "No endpoint to edit an entry.", "category": "missing-capability"}' \
  https://tallymeter.com/api/v1/feedback

MCP — plug your agent in directly

Tallymeter is also a native MCP server (Streamable HTTP) at https://tallymeter.com/mcp, authenticated with the same Bearer tokens. Beyond the timer and projects it adds log-time (file finished work after the fact), update-entry (fix a running or finished entry's description in place — both need entries:write) and unbilled-summary (what is ready to invoice, needs reports:read) — so an agent can do the work, log the time it spent, and tell you what to bill.

# Claude Code
claude mcp add --transport http tallymeter https://tallymeter.com/mcp \
  --header "Authorization: Bearer $TOKEN"

Tools: timer-status, start-timer, stop-timer, list-projects, log-time, update-entry, unbilled-summary, send-feedback.

Prefer a ready-made setup? The open-source Tallymeter Agent Skill (MIT) teaches Claude Code and compatible harnesses the whole track→log→bill workflow:

git clone https://github.com/mkantautas/tallymeter-skill ~/.claude/skills/tallymeter

Conventions