New: watch your coding agents work, step by step, on a live board. Try the demo, no account

Docs / Reference

Events and the live stream

Every create, status change, plan, progress report, note, edit and delete writes an event with an increasing id. Read them two ways: poll /api/events with a cursor, or hold open /api/events/stream and receive them as Server-Sent Events.

Cursor feed

curl -s "https://taskhold.com/api/events?since=26" -H "Authorization: Bearer API_KEY"
{"events": [
  {"id": 27, "type": "task.status", "actor": "agent", "actor_name": "GitHub Actions (deploy.yml)",
   "task_id": "0a017133-...", "root_task_id": "fd11278a-...", "task_title": "Build images", "summary": "Started",
   "data": {"from": "todo", "to": "in_progress", "blocked_reason": null, "completed_summary": null},
   "created_at": "2026-10-26T05:09:47.358Z"},
  {"id": 28, "type": "task.progress", "summary": "60% (3/5) Building api image", ...}
],
 "cursor": 28}
  • since: return events after this id, oldest first. Without it you get the latest limit events (default 20).
  • actor=human or actor=agent: only one side's changes. taskhold_updates uses actor=human.
  • task_id: only events on this task and its steps.
  • limit: up to 500. Pass the returned cursor as since next time.

Event types

Event types
typeWhen
task.createdA task or step was created
subtasks.plannedSteps were added with a plan
task.statusStatus changed; data.from and data.to, plus blocked_reason or completed_summary
task.progressProgress reported; summary like "60% (3/5) Building api image"
task.noteA note was added (replies to blocked tasks arrive this way)
task.updatedTitle, description, priority or project changed
task.deletedA task was deleted

Progress events older than 14 days are pruned; the rest stay.

Server-Sent Events

The stream starts at the current moment (or after Last-Event-ID, or ?since=), sends a ready event with the cursor, then one event: task frame per change. A comment line every 15 seconds keeps proxies from closing it, and the server ends each stream after 10 minutes; reconnect with Last-Event-ID and nothing is lost.

retry: 3000
event: ready
data: {"cursor":26}

id: 27
event: task
data: {"id":27,"type":"task.status","actor":"agent","summary":"Started","task_title":"Build images",...}

Watch from a terminal

One line per change, with curl and jq:

curl -sN https://taskhold.com/api/events/stream -H "Authorization: Bearer API_KEY" \
  | sed -un 's/^data: //p' \
  | jq -r --unbuffered 'select(.summary) | "\(.created_at[11:19])  \(.actor_name // .actor)  \(.task_title // ""): \(.summary)"'
05:09:47  GitHub Actions (deploy.yml)  Build images: Started
05:09:47  GitHub Actions (deploy.yml)  Build images: 60% (3/5) Building api image

Browsers can't set an Authorization header on EventSource; in a browser, the signed-in web app uses its session cookie. From scripts, read the stream with fetch as in /docs/slack-alerts.

Live snapshot

GET /api/live returns what the board shows in one call: active (in progress, with steps), blocked, recently_done (last 2 hours), up_next, counts (in_progress, blocked, todo, done_today), the latest events and the cursor to stream from.

Read next

Updated 2026-10-26 · Markdown for agents · Questions: hello@greatwork.company