Skip to main content

HTTP API Overview

td serve runs a local HTTP API server that exposes td's issue tracker over REST. It provides JSON endpoints for creating, reading, updating, and managing issues, boards, sessions, and more -- designed for building web UIs and integrations on top of td.

Starting the Server

td serve

This starts the server on a random available port. The actual port is printed to stderr and written to a discovery file.

td serve listening on http://localhost:54321
base dir: /Users/marcus/code/td
database: /Users/marcus/code/td/.todos/issues.db
session: ses_a1b2c3 (web)
port file: /Users/marcus/code/td/.todos/serve-port

Flags

FlagDefaultDescription
-p, --port0 (auto)Port to listen on
-a, --addrlocalhostAddress to bind to
--token(none)Bearer token for authentication
--cors(none)Allowed CORS origin for browser clients
--interval2sPoll interval for SSE change detection

Examples

# Auto-assign port (default)
td serve

# Fixed port
td serve --port 8080

# With auth and CORS for a React dev server
td serve --token my-secret --cors http://localhost:3000

Discovery Mechanism

Each td serve process writes a JSON port file at .todos/serve-port for programmatic discovery:

{
"port": 54321,
"pid": 91234,
"started_at": "2026-02-27T05:10:11Z",
"instance_id": "srv_8f3b2c"
}

Consumer Discovery Flow

  1. Read .todos/serve-port and parse the JSON.
  2. Call GET /health on the recorded port.
  3. Reuse the process when healthy.
  4. Start td serve when the file is missing, the PID is dead, or /health fails.
tip

Run one td serve process per td project. Do not coordinate multiple projects inside one server instance.

Response Format

All endpoints return a standard JSON envelope.

Success:

{ "ok": true, "data": { ... } }

Error:

{
"ok": false,
"error": {
"code": "validation_error",
"message": "request validation failed",
"details": {
"fields": [
{
"field": "title",
"rule": "min_length",
"value": 2,
"expected": 3,
"message": "title must be at least 3 characters"
}
]
}
}
}

Error Codes

CodeHTTP StatusDescription
validation_error400Invalid request data
unauthorized401Missing or invalid auth token
forbidden403Access denied
not_found404Resource does not exist
conflict409Invalid state transition
internal500Server error

JSON Serialization Rules

The API enforces consistent JSON output:

  • All documented keys are always present in responses.
  • Unset optional references and timestamps serialize as null.
  • Collections serialize as [] when empty, never null.
  • Freeform text fields serialize as "" when empty.

Session Model

The server uses a single web session for write attribution. This session is created automatically on startup with:

  • agent_type = "web"
  • name = "td-serve-web"
  • branch = "default"

All writes through the HTTP API are attributed to this session. The session's last_activity is bumped periodically while the server is running.

Graceful Shutdown

The server handles SIGINT and SIGTERM gracefully:

  1. Stops accepting new requests.
  2. Drains in-flight requests.
  3. Closes SSE client connections.
  4. Deletes the .todos/serve-port file.
  5. Force-closes after a 10-second timeout.