REST API reference
The headless task API lets scripts and AI agents read and manage a workspace's tasks. Authentication uses API tokens.
Base URL & authentication
The base URL is your deployment's *.convex.site address — the Workspace → API
tokens page shows it in a ready-made snippet. Every request sends the token as a
bearer:
curl -H "Authorization: Bearer yk_..." \
"https://<deployment>.convex.site/api/app/tasks"Errors & rate limits
Errors are JSON with a single error field:
| Status | Meaning |
|---|---|
400 |
Invalid input — the message says what's wrong |
401 |
Missing, invalid, expired, or revoked token |
403 |
Token lacks the required scope |
404 |
Task not found (or not in this token's workspace) |
429 |
Rate limit exceeded — retry after Retry-After seconds |
Each token gets 120 requests per minute. Error messages are written to be readable by LLMs, so agents can self-correct.
GET /api/app/tasks
Scope: tasks:read. Paginated list of the workspace's tasks.
Query parameters:
projectId— only tasks in this project.includeDone—trueto include tasks in terminal columns (default: excluded).limit— page size, 1–200 (default 50).cursor— cursor from the previous page.
curl -H "Authorization: Bearer $YOKE_TOKEN" \
"$BASE/api/app/tasks?limit=50&includeDone=false"{
"tasks": [
{
"id": "jd7c...",
"title": "Send the invoice to Acme",
"description": null,
"priority": "high",
"priorityValue": 3,
"status": { "name": "To do", "isTerminal": false },
"project": { "id": "jx2a...", "name": "Inbox" },
"assignee": null,
"labels": [{ "id": "jl9k...", "name": "urgent", "color": "red" }],
"dueDate": 1752192000000,
"dueDateString": "2026-07-11",
"createdAt": 1751884800000,
"source": "manual"
}
],
"cursor": "eyJ...",
"isDone": false
}Pagination notes: done-filtering happens after pagination, so a page can be smaller
than limit — keep following cursor until isDone is true. Priority sorting
applies within a page.
GET /api/app/tasks/:id
Scope: tasks:read. One task with its comments and attachments. Returns
{ "task": ... }, or 404 if the task isn't in the token's workspace.
POST /api/app/tasks
Scope: tasks:write. Creates a task; responds 201 with the created task.
Body fields:
title(required) — string.description— string.priority—1–4or"low"/"medium"/"high"/"critical"(4/critical shows as "urgent" in the app). Default: low.projectId— target project (default: Inbox). Get ids from/api/app/meta.dueDate— epoch milliseconds.labelIds— array of label ids.
curl -X POST -H "Authorization: Bearer $YOKE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Review the PR", "priority": "high", "dueDate": 1752192000000}' \
"$BASE/api/app/tasks"PATCH /api/app/tasks/:id
Scope: tasks:write. Partial update — only the fields present in the body change;
sending null clears a clearable field.
title— string (non-empty).description— string, ornullto clear.priority— number or string, as in create.dueDate— epoch ms, ornullto clear.projectId— project id, ornullto clear.labelIds— replaces the whole set; include current labels to keep them.status— a column name, matched case-insensitively ("in progress"). Unknown names return400listing the available columns. Moving to a terminal column marks the task completed.
curl -X PATCH -H "Authorization: Bearer $YOKE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": "In progress", "priority": "critical"}' \
"$BASE/api/app/tasks/jd7c..."Returns the updated task as { "task": ... }.
POST /api/app/tasks/:id/comments
Scope: tasks:write. Adds a comment authored by the token's owner.
curl -X POST -H "Authorization: Bearer $YOKE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"body": "Deployed to staging, please verify."}' \
"$BASE/api/app/tasks/jd7c.../comments"Responds 201.
GET /api/app/meta
Scope: tasks:read. The workspace's projects, labels, and statuses — fetch this
first to map names to ids.
{
"projects": [{ "id": "jx2a...", "name": "Inbox", "color": "gray" }],
"labels": [{ "id": "jl9k...", "name": "urgent", "color": "red" }],
"statuses": [
{ "id": "js3f...", "name": "To do", "color": "gray", "isTerminal": false },
{ "id": "js8h...", "name": "Done", "color": "green", "isTerminal": true }
]
}