Taskr API v1, from a script and from an AI assistant
Everything the interface can do to your workspace has a token-authenticated REST API behind it:
workspaces, lists, tasks, notes, tracked time, statistics and stored files. There are two doors into
the same surface — write the automation yourself against https://taskr.cc/api/v1, or let an AI
assistant drive exactly the same endpoints as MCP tools. Both read one registry, so an endpoint
cannot exist in one door and be missing in the other.
API access is part of Premium Plus and Team. Free and Premium accounts see an upgrade panel
on the API settings tab instead of the token list, and every API call from such an account answers
403 plan_required with a link to the plans.
Before your first call
- A plan with API access (Premium Plus or Team) on the account that owns the token.
- A verified email. An unverified credentials account cannot write through the API — the same rule the app applies to any write.
- A realistic expectation of what a token can do. A token is not a separate identity: it acts as the person who created it. Workspace roles still apply (a VIEWER token cannot edit tasks), quotas and limits are still checked against the workspace owner's plan, and if that plan loses API access — a downgrade, an expired subscription — the token stops working on the next request.
1. Create a token
Open Settings → API (/settings?tab=api) and press Create token. The dialog asks for three
things:
| Field | What to pick |
|---|---|
| Name | A label you will recognise in the list later — Hermes, deploy-script, weekly-invoice. It is not a username; it changes nothing about what the token can do. |
| Scope | Read only (read) can list workspaces, lists, tasks, notes, statistics and files. Read and write (read + write) can additionally create, update and complete tasks and lists, write notes, start and stop timers and upload files. write implies read. |
| Expires | Never, or 30 / 90 / 365 days. An expired token stops authenticating on its own and stops counting against your token limit. |
Copy the token immediately. The plaintext (tk_live_…) is shown exactly once, in the
confirmation dialog. The database stores only a hash of it, and the list afterwards shows just the
prefix (tk_live_ab12…), the scopes, when it was created and when it was last used. If you lose it,
revoke it and create another one — there is nothing to recover.
Revoking is immediate. The row stays in the list, greyed out as REVOKED, and the very next
request with that token answers 401. Dead tokens (revoked or expired) are kept for 30 days so you
can still see what you switched off, then removed automatically.
One account can hold 10 usable tokens at a time — revoked and expired ones do not count. The
Create token button refuses before the dialog opens once you are at the cap, with an Upgrade
plan link in the toast; the API answers 403 limit_reached.
2. Your first call
Put the token in an environment variable rather than in your shell history:
export TASKR_TOKEN="tk_live_…"
curl -s https://taskr.cc/api/v1/me \
-H "Authorization: Bearer $TASKR_TOKEN"
curl -s https://taskr.cc/api/v1/workspaces \
-H "Authorization: Bearer $TASKR_TOKEN"
GET /me answers with who the token belongs to (user), the token itself as metadata (prefix,
scopes, expiresAt, lastUsedAt — never the token), the plan (tier, mcpApi, limits) and the
two rate-limit budgets every call is checked against, so a client never has to guess its headroom.
GET /workspaces is where every other call starts, because almost every endpoint needs a workspace
id. It returns the workspaces you own or belong to, with your role in each and counters for lists,
tasks, notes and other members.
If that returned data, you are done — the rest is reading the endpoint list at the bottom of this
page and calling it. A 401 means the token is missing, malformed, revoked or expired; a
403 plan_required means the account's plan has no API access; a 429 carries Retry-After.
3. What the API can and cannot do
Can: read workspaces; create, rename, reorder and delete lists; create, edit, complete, reopen, move and delete tasks; add subtasks; read and write notes; search across a workspace; resolve a pasted id to the thing it names; read statistics for tasks, lists and tracked time; start and stop the time tracker; list, upload and delete stored files.
Cannot, in v1: create workspaces, invite or manage members, change roles, touch billing or account settings, or read your notification history. Those live in the interface only — deliberately, because they are the decisions a person should be making by hand. If you need one of them automated, say so; the list of what is worth opening up is short and driven by what people actually ask for.
4. Conventions worth knowing before you write code
- Pagination. List endpoints take
limit(1–200, default 50) andcursor, and answer{ "data": [ … ], "nextCursor": "…" }.nextCursorisnullon the last page; it is an opaque keyset cursor, so never build one yourself. Rows are ordered newest first.searchis the exception — it caps matches per type instead of paging. - Ids travel in a paste-ready form. Every id parameter accepts the bare id the API returns and a
reference — the same id with a type prefix:
taskr_w_…(workspace),taskr_l_…(list),taskr_t_…(task),taskr_n_…(note). The app's copy buttons put the prefixed form on your clipboard, so an id lifted off the board can be pasted straight into a request. A prefix is an alias, never a permission: a reference to someone else's row answers the same404a bare id does. - Ids you cannot see answer
404, not403. A foreign workspace, another person's private note and a genuinely missing id are indistinguishable by design — otherwise the API would tell a stranger which ids exist. Never branch your client on403vs404to learn whether something is real; both mean "not available to you". - Deletes behave like the interface. A deleted list or task is soft-deleted: it stays in the
database with its real name and a
deleted: trueflag, and is hidden from reads unless you ask for it (list_liststakesincludeDeleted=true). Statistics are the one place where soft-deleted tasks still appear, so tracked hours stay attributable. - List rows stay small.
list_taskstruncatestextat 500 characters and tells you there is a description; the full markdown is onget_taskalone. Same for notes:list_notescarries a short preview,get_notecarries the body. A page of 200 rows must never be able to ship megabytes. - Notes keep their rules. A private note is visible only to its author (
404for everyone else), and a locked note refuses content writes with403. - Writes update open boards, and never your phone. An API write emits the same realtime event and workspace activity entry the app emits, so a board someone is watching updates without a refresh. It deliberately sends no push notification and no notification row — an agent writing in bulk must not ring anybody.
- and are inclusive day keys () in your timezone and default to the last 90 days. OWNER/MODERATOR see the whole workspace, VIEWER/EDITOR only their own tracked time, and the answer says which applied.
5. Answers and limits you will run into
Every failure, on every endpoint, comes back in one envelope, and requestId is what to quote if you
need help:
{
"error": {
"code": "not_found",
"message": "Task not found",
"requestId": "req_9f3c1a0b7d2e4c88"
}
}
code | HTTP | Meaning |
|---|---|---|
invalid_input | 400 | The body or query failed validation; error.details names the offending field. |
unauthorized | 401 | Missing, malformed, revoked or expired token. |
forbidden | 403 | Your role in that workspace does not allow the action (a locked note answers this too). |
plan_required | 403 | The plan gates it: no API access, no time tracking, or a plan limit for the resource is reached. error.details.upgradeUrl says where to fix it. |
not_found | 404 | Does not exist, or is not visible to this token. |
conflict | 409 | Conflicts with the current state — tracking a completed task, for instance. |
payload_too_large | 413 | Over the body ceiling: 1 MiB of JSON, 6 MiB for an upload, 7.67 MiB for one MCP request. |
rate_limited | 429 | A budget is exhausted. Honour Retry-After. |
internal | 500 | Something broke on our side; details are never exposed. Quote the requestId. |
Rate limits are two budgets plus one fuse. Every request is checked against the token's own budget (600 requests available immediately, then a sustained 10/s) and the account's budget (1200 immediately, then 10/s, shared by every token on the account). A separate, much higher per-IP fuse sits in front of both, and a stream of failed authentication from one address is throttled on its own budget (60 refusals per minute) so that guessing at tokens stops being cheap.
Both budgets are on every answer, so you never have to guess which one refused you — the family whose
Remaining reached 0 is the one that did:
| Header | Meaning |
|---|---|
X-RateLimit-Token-Limit / -Remaining / -Reset | This token's budget (600, refilling 10/s). |
X-RateLimit-Account-Limit / -Remaining / -Reset | The account's budget, shared by all its tokens (1200, refilling 10/s). |
X-RateLimit-Failure-* | The per-IP refusal budget (60/min, refilling 1/s). |
Retry-After | Present on every 429 from those budgets. |
Read them as token buckets rather than "N per minute": 600 now, then 10/s. Creating another token gives that new token its own 600 — it does not add to the account's shared 1200, so minting tokens to get more throughput does not work. Plan for the sustained rate: an IDE client plus a cron job never comes near the ceiling, a loop that hammers the API will meet it.
Every response also carries X-Request-Id and Cache-Control: no-store. Refusals by the plan or
scope gate carry no budget headers at all, because nothing was counted yet — and a 401 carries none
either, on purpose: an unauthenticated caller should not be able to measure your budgets.
6. Handing the same surface to an AI assistant
The whole API is available as MCP tools: one tool per endpoint, with the same names, schemas, scopes and gates. There is nothing to install for the hosted endpoint — add a URL and your token:
// Claude Code (.mcp.json) and Cursor (~/.cursor/mcp.json) — same name, same token
{
"mcpServers": {
"taskr": {
"type": "http",
"url": "https://taskr.cc/api/mcp",
"headers": { "Authorization": "Bearer tk_live_…" }
}
}
}
# Hermes — ~/.hermes/config.yaml
mcp_servers:
taskr:
url: "https://taskr.cc/api/mcp"
headers:
Authorization: "Bearer tk_live_…"
The endpoint is stateless — one server per request, no session, no sticky routing — so it works behind any proxy, and a plain HTTP client is enough to speak to it. Four things worth knowing:
- A tool call costs what a REST call costs. The protocol request authenticates once and every tool call inside it is a full API call, so budget roughly half the requests of the same token's REST allowance when each request carries a call.
- Keep one call under two minutes. A single call that takes longer than the edge's read timeout
(125 s) is cut off with a
524. Long aggregates belong in a REST job you poll. - MCP passes the same gates. A
readtoken can list the tools and is refused, per call, on every write tool — exactly as over REST. Free and Premium accounts get403 plan_required. - A local stdio server exists too, for development against this repository (
mcp/), but it is not published to npm yet, so there is nonpx -y @taskr/mcpto install. Use the hosted endpoint.
Every endpoint, generated from the API itself
The list below is grouped the way you would look for something. It is not written by hand: it is rendered from the API's own registry while this page is built, so it cannot describe an endpoint that does not exist, and it keeps up with a new one without anyone remembering to update a table.
34 endpoints — 17 reads and 17 writes. Not typed by hand: rendered while this page is built from the API’s own registry, the same one that serves the requests, so an endpoint cannot appear here that does not exist — GET /_tools returns the same catalog as JSON, with each endpoint’s full input schema, for anything that reads it mechanically.
Account and catalog
| Endpoint | What it does |
|---|---|
GET /_toolsreadlist_tools | List every tool this API exposes, with its REST method and path, its input JSON Schema, the required token scope and whether it is destructive. An MCP client calls this once at startup to register its tools, so a newly added endpoint needs no client update. Requires a read scope; the plan gate applies. |
GET /mereadget_me | Return the identity behind the API token: the calling user, the token's prefix and scopes, its expiry, the plan tier with its limits, and the two rate-limit budgets every call is checked against — the per-token one and the per-account one that all of the account's tokens share. Use it first to confirm which account a token belongs to, and to size concurrency without guessing. It never returns the token itself. |
Workspaces
| Endpoint | What it does |
|---|---|
GET /workspacesreadlist_workspaces | List the workspaces the token's user owns or belongs to, newest first. Use it first: every other tool needs a workspace id, and this is where you get one. Each entry carries the caller's role/isOwner and the counters lists/tasks/notes/members — counts only notes the caller may see, and exclude soft-deleted rows, and counts collaborators OTHER than the owner (the owner has no membership row, so 0 means the workspace is not shared). Only workspaces the caller may access are returned; a foreign workspace is simply absent. is truncated at 500 characters in list entries — returns the stored value. Paginate with the opaque until is null. |
Ready when you are.
Create an API tokenLast checked against the app: 2026-10-16