The Public API v2 is an action-based API. Each operation sends a JSON body with POST to a named endpoint.
It is not RESTful. It has no GET /workspaces or PUT /tasks/{id} endpoints.
Media downloads, bundle downloads, and webhook registration are the REST-style exceptions.
v2 is in beta (2.0.0-beta) and runs alongside v1. It addspromptAgentand signed webhook registration.Tasks are fully writable in v2.
Content:
createTask,updateTask,deleteTask,moveTask,completeTask,uncompleteTask.Metadata:
assignTask,unassignTask,setTaskDate,deleteTaskDate,setTaskNote,deleteTaskNote,setTaskFieldValue,deleteTaskFieldValue.v1 has the longest track record. It alone reads a single task, at
GET /projects/{projectId}/tasks/{taskId}.Neither version renames or deletes a project. See Choose an API.
The live OpenAPI spec is published at taskade.com/api/documentation/v2.
Table of Contents
- Choose an API
- Base URL & Authentication
- Calling convention
- Endpoints
- Pagination
- Rate Limits
- Error Handling
- Security Best Practices
Choose an API
Taskade ships two public HTTP APIs. They share the same authentication.
| REST API v1 | Action API v2 | |
|---|---|---|
| Base URL | https://www.taskade.com/api/v1 |
https://www.taskade.com/api/v2 |
| Style | RESTful (GET/POST/PUT/DELETE) |
Action / RPC (POST /{operation}) |
| Status | Stable (GA) | Beta (2.0.0-beta) |
| Live spec | /api/documentation/v1 | /api/documentation/v2 |
| Task create / update / delete | ✅ Full CRUD | ✅ Full CRUD (createTask, updateTask, deleteTask, moveTask) |
| Task assignees / dates / notes / fields | ✅ | ✅ (assignTask, setTaskDate, setTaskNote, setTaskFieldValue, …) |
| Read a single task, date, note or field | ✅ GET /projects/{projectId}/tasks/{taskId}, …/date, …/note, …/fields/{fieldId} |
❌ list-only (listTasks, listBlocks, listFields) |
| Project update, rename, or delete | ❌ (GET /projects/{projectId} is read-only) |
❌ (create / complete / restore / copy only) |
| Prompt an agent | ❌ | ✅ promptAgent |
| Agent lifecycle (create/update/delete) | ✅ | ✅ |
| Bundles (export/import Taskade Genesis apps) | ✅ | ✅ |
| Signed webhook registration | ❌ | ✅ POST /webhooks |
| Reference | API reference | This page |
Use v2 for LLM tools, agent prompts, or webhook registration. One verb per endpoint maps directly to a tool definition.
Use v1 for its longer stability record. Also use v1 to read one task, date, note, or field directly.
Every row in this table is checkable. Each row comes from the two live OpenAPI documents. The generated Action API reference and REST API reference are built from those same specs.
Base URL & Authentication
All v2 operations live under:
https://www.taskade.com/api/v2
Authenticate with a Personal Access Token from taskade.com/settings/api, or an OAuth 2.0 access token for apps that act on behalf of other users:
Authorization: Bearer YOUR_TOKEN
See the Authentication guide for personal tokens vs. OAuth 2.0 (PKCE) details.
Calling convention
Every v2 call sends a JSON body with POST to an operation name. The API returns JSON.
Successful responses use { "ok": true, ... }.
Here is a single Action API v2 call, from authentication to JSON response.
curl -X POST https://www.taskade.com/api/v2/OPERATION \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "param": "value" }'
A typical response:
{ "ok": true, "items": [ /* ... */ ] }
Receiving events: Register a signed outbound webhook withPOST /api/v2/webhooks.Taskade signs every delivery with an HMAC secret. Make sure that each signature is valid.
See the Webhook Registration API. The older
subscribeWebhookandunsubscribeWebhookoperations are deprecated.
Endpoints
List spaces (workspaces)
POST /listSpaces is every integration's entry point. Body is optional.
cURL
curl -X POST https://www.taskade.com/api/v2/listSpaces \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
Python
import requests
res = requests.post(
"https://www.taskade.com/api/v2/listSpaces",
headers={"Authorization": "Bearer YOUR_TOKEN"},
json={},
)
print(res.json()["items"])
TypeScript
const res = await fetch("https://www.taskade.com/api/v2/listSpaces", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TASKADE_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
const { items } = await res.json();
Filter with { "filterBy": { "name": { "operator": "contains", "value": "Marketing" } } }.
List folders in a space
POST /listFolders
curl -X POST https://www.taskade.com/api/v2/listFolders \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "spaceId": "SPACE_ID" }'
List projects in a space
POST /listProjects
curl -X POST https://www.taskade.com/api/v2/listProjects \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "spaceId": "SPACE_ID" }'
Get a project
POST /getProject returns { ok, item: { id, name } }.
curl -X POST https://www.taskade.com/api/v2/getProject \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "projectId": "PROJECT_ID" }'
Create a project
POST /createProject seeds a project from Markdown.
cURL
curl -X POST https://www.taskade.com/api/v2/createProject \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"spaceId": "SPACE_ID",
"contentType": "text/markdown",
"content": "# Q2 Planning\n\n- Review roadmap\n- Draft OKRs"
}'
Python
import requests
res = requests.post(
"https://www.taskade.com/api/v2/createProject",
headers={"Authorization": "Bearer YOUR_TOKEN"},
json={
"spaceId": "SPACE_ID",
"contentType": "text/markdown",
"content": "# Q2 Planning\n\n- Review roadmap\n- Draft OKRs",
},
)
print(res.json()["item"]["id"])
List tasks
POST /listTasks is paginated with after / before cursors. Each task is { id, text, parentId?, completed }.
curl -X POST https://www.taskade.com/api/v2/listTasks \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "projectId": "PROJECT_ID", "limit": 100 }'
v2 reads and writes tasks.To change a task, see
createTask,updateTask, andmoveTask.To set metadata, see
assignTask,setTaskDate,setTaskNote, andsetTaskFieldValue.To read one task, use the REST API v1 Tasks endpoints.
If Google Calendar is connected on an eligible workspace, dates set with setTaskDate can sync the same way as dates set in Taskade.
Prompt an agent
POST /promptAgent sends a single prompt to a workspace agent. The endpoint returns a synchronous text response. This capability is v2-only.
cURL
curl -X POST https://www.taskade.com/api/v2/promptAgent \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"spaceId": "SPACE_ID",
"agentId": "AGENT_ID",
"prompt": "Summarize yesterday'\''s standup notes"
}'
Python
import requests
res = requests.post(
"https://www.taskade.com/api/v2/promptAgent",
headers={"Authorization": "Bearer YOUR_TOKEN"},
json={
"spaceId": "SPACE_ID",
"agentId": "AGENT_ID",
"prompt": "Summarize yesterday's standup notes",
},
)
print(res.json()["summary"])
TypeScript
const res = await fetch("https://www.taskade.com/api/v2/promptAgent", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TASKADE_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ spaceId, agentId, prompt: "Summarize standup notes" }),
});
const { summary } = await res.json();
Response:
{ "ok": true, "summary": "Here's a summary of the standup..." }
To list past conversations, use POST /listConversations with { agentId, limit?, page? }.
To read one, use POST /getConversation with { agentId, convoId, includeTranscript? }.
Pass "includeTranscript": true to add a Markdown transcript field to the response.
Manage agents
| Operation | Body |
|---|---|
POST /listAgents |
{ spaceId, filterBy? } |
POST /getAgent |
{ agentId } |
POST /createAgent |
{ folderId, name, data } |
POST /updateAgent |
{ agentId, name?, data? } |
POST /deleteAgent |
{ agentId } |
POST /generateAgent |
{ folderId, text } - generate an agent from a description |
POST /enablePublicAgentAccess |
{ agentId } → { ok, publicUrl } |
Attach knowledge to an agent
POST /addKnowledgeProject grounds an agent in a project. (removeKnowledgeProject, addKnowledgeMedia, removeKnowledgeMedia mirror it.)
curl -X POST https://www.taskade.com/api/v2/addKnowledgeProject \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "agentId": "AGENT_ID", "projectId": "PROJECT_ID" }'
Export / import a bundle
POST /exportBundle returns a portable Taskade Genesis app bundle. POST /importBundle installs one.
See Bundles & App Kits for the full schema and binary .tsk variants.
curl -X POST https://www.taskade.com/api/v2/exportBundle \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "spaceId": "SPACE_ID" }'
Full operation list
- Workspaces & structure:
listSpaces,listFolders,listMyProjects,listTemplates,listMedia. - Projects:
listProjects,getProject,createProject,createProjectFromTemplate,copyProject,completeProject,restoreProject,listBlocks,listFields,listProjectMembers,getShareLink,enableShareLink. - Tasks, content:
listTasks,createTask,updateTask,deleteTask,moveTask,completeTask,uncompleteTask. - Tasks, metadata:
assignTask,unassignTask,setTaskDate,deleteTaskDate,setTaskNote,deleteTaskNote,setTaskFieldValue,deleteTaskFieldValue. - Agents, lifecycle:
listAgents,getAgent,createAgent,updateAgent,deleteAgent,promptAgent,generateAgent. - Agents, conversations:
listConversations,getConversation. - Agents, knowledge:
addKnowledgeProject,removeKnowledgeProject,addKnowledgeMedia,removeKnowledgeMedia. - Agents, public access:
enablePublicAgentAccess,getPublicAgent,updatePublicAgent. - Media:
uploadMedia,getMedia,deleteMedia, plusGET /media/{mediaId}/contentandGET /media/spaces/{spaceId}/contentfor downloads. - Bundles:
exportBundle,importBundle,importBundleZip, plusGET /bundles/{spaceId}/export/zip. - Webhooks:
POST /webhooks,GET /webhooks,GET /webhooks/{id},DELETE /webhooks/{id}. See Webhooks.subscribeWebhookandunsubscribeWebhookare deprecated.
The authoritative, always-current list is the live v2 spec.
Pagination
List operations that can return many rows use cursor pagination with after / before (tasks, blocks) or page / limit (members, conversations).
# next page of tasks
curl -X POST https://www.taskade.com/api/v2/listTasks \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "projectId": "PROJECT_ID", "limit": 100, "after": "LAST_TASK_ID" }'
Rate Limits
Every response carries the current budget in three headers. Header names are case-insensitive:
| Header | Meaning |
|---|---|
X-Rate-Limit-Limit |
Your total budget for the current window |
X-Rate-Limit-Remaining |
Requests left in the window (0 when you are blocked) |
X-Rate-Limit-Reset |
Seconds until the window reopens. Schedule your retry from this |
Read the headers rather than a fixed number. Taskade can change a ceiling without a docs release.
v2 operations share the public API's per-IP budget with every other public request. Taskade does not publish the exact ceiling, and it can change without a docs release. Read the X-Rate-Limit-* headers on each response for the live budget. A block lasts until the window reopens.
Taskade does not send Retry-After. Before you retry, wait for X-Rate-Limit-Reset. Earlier retries are also rejected.
async function withRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
for (let i = 0; i < retries; i++) {
try {
return await fn();
} catch (err: any) {
if (err.status === 429 && i < retries - 1) {
const resetSec = Number(err.headers?.["x-rate-limit-reset"]);
const waitMs = resetSec > 0 ? resetSec * 1000 : 2 ** i * 1000;
await new Promise(r => setTimeout(r, waitMs));
continue;
}
throw err;
}
}
throw new Error("Retry exhausted");
}
For sustained throughput, batch operations such as /createTask, which takes a tasks array. Reduce polling frequency. Stagger scheduled jobs.
Error Handling
Every error response carries ok: false, message, code, and statusMessage:
{
"ok": false,
"message": "Project not found",
"code": "NOT_FOUND",
"statusMessage": "Not Found"
}
A schema validation failure uses code: "FST_ERR_VALIDATION" and includes the same four fields. Branch on ok and code. Do not parse the human-readable message.
| Status | Meaning | Retry? | Fix |
|---|---|---|---|
| 400 | Bad request | No | Examine the request body |
| 401 | Invalid / missing token | No | Regenerate or refresh the token |
| 402 | The operation is not available for the current account or workspace | No | Examine the operation requirements and current access |
| 403 | Insufficient permission | No | Use a token with access to the resource |
| 404 | Not found | No | Make sure that the ID and workspace access are correct |
| 429 | Rate limited | Yes | Exponential backoff |
| 5xx | Server error | Yes | Retry up to 3 times with backoff |
Never retry on400,401,403, or404. Fix the request first.
Security Best Practices
- Never commit tokens. Use environment variables or a secret manager.
- Use OAuth, not personal tokens, for multi-user applications.
- Rotate personal tokens periodically. You can hold up to 5 at a time.
- Encrypt refresh tokens at rest. They are long-lived.
Related
→ Webhooks