API overview
Spantail is built around a single REST API. The web app, the CLI, and the MCP server are all clients of the same endpoints — anything they can do, you can do directly over HTTP.
Base URL
Section titled “Base URL”The REST API lives under /api/v1 on your instance:
Replace <your-instance> with your own Worker domain (its *.workers.dev URL or a custom
domain). A few endpoints sit outside /api/v1: authentication under
/api/auth (see Authentication), the health check
at /api/health, and the public share views under /share.
Stability
Section titled “Stability”Spantail is pre-1.0, so /api/v1 may still change in breaking ways between releases.
From v1.0.0 on, /api/v1 only grows: endpoints and fields may be added, but never removed
or repurposed. A breaking change would ship as /api/v2 alongside the existing /api/v1,
which keeps working. Write clients that ignore response fields they do not recognize.
Every /api response carries the instance’s version in the x-spantail-version header, so a
client can tell which server it is talking to without an extra request.
Conventions
Section titled “Conventions”- JSON by default. Request and response bodies are
application/json; charset=utf-8; sendContent-Type: application/jsonon every request with a body. A few endpoints return binary instead (e.g. avatar and workspace-logo image bytes), as noted on their pages. - Timestamps are ISO 8601 strings in UTC (e.g.
2026-06-28T09:30:00.000Z). - Dates are calendar-day strings
YYYY-MM-DD, interpreted in the relevant user’s timezone (see Dates and time). - Durations are integer minutes.
- IDs are opaque strings. Do not parse or construct them.
- Unknown fields in a request body are rejected — bodies are validated strictly.
Authentication
Section titled “Authentication”Every endpoint except a handful of public ones (health, share views, sign-in, accepting an invitation) requires authentication. You can authenticate with a session cookie, a Personal Access Token, or an Agent Access Token. See Authentication for the full model, token formats, and scopes.
Dates and time
Section titled “Dates and time”Timezone is a per-user concept (user.timezone, null means UTC). Workspaces and projects
have none. A date and a timestamp are independent:
- A work entry’s
entryDateis a local date string in the author’s timezone, frozen at write. - All timestamps are UTC instants; durations are integer minutes.
- Agent entries store only timestamps; their calendar day is derived from
startedAtin the viewer’s timezone at read time. - Reports resolve relative ranges and the generation date in the running user’s timezone.
Pagination
Section titled “Pagination”List endpoints that can return large result sets accept limit and offset query
parameters. The default when limit is omitted is endpoint-dependent — some apply a default
and cap (for example, work entries default to 50 and cap at 200), while others treat
limit as optional and return the full set; each endpoint page notes its behavior. Results
are returned as a plain JSON array; page by incrementing offset.
Filtering
Section titled “Filtering”List endpoints accept resource-specific query parameters — typically workspaceId
(required), plus optional projectId, userId, tag, and a from/to date range. The
exact parameters are documented on each resource page.
Errors
Section titled “Errors”Errors are returned with the appropriate HTTP status and a structured JSON body. Stack traces are never exposed.
| Code | HTTP | Meaning |
|---|---|---|
bad_request |
400 | Malformed or invalid request (failed validation, illegal state change). |
unauthorized |
401 | Missing, invalid, or expired credential. |
forbidden |
403 | Authenticated but not allowed to perform the action. |
insufficient_scope |
403 | The Personal Access Token lacks the required scope. |
not_found |
404 | The resource does not exist, or is hidden from the caller. |
conflict |
409 | The request conflicts with current state (e.g. duplicate slug). |
rate_limited |
429 | Too many requests for this credential. Back off and retry. |
internal |
500 | Unexpected server error. |
Rate limiting
Section titled “Rate limiting”Write-heavy and ingest endpoints — creating work entries, ingesting agent activity — are
rate-limited per credential (per token, or per user for sessions). Exceeding the limit
returns 429 with code rate_limited. Back off and retry.
Health check
Section titled “Health check”GET /api/health public returns { "status": "ok" } with no
authentication. Use it for uptime monitoring.