All topics

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.
  • includeDonetrue to 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.
  • priority14 or "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, or null to clear.
  • priority — number or string, as in create.
  • dueDate — epoch ms, or null to clear.
  • projectId — project id, or null to clear.
  • labelIdsreplaces the whole set; include current labels to keep them.
  • status — a column name, matched case-insensitively ("in progress"). Unknown names return 400 listing 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 }
	]
}