Build integrations with the Taskade REST API v1. Access projects, tasks, agents, and media through HTTP. Tasks support full CRUD.
Page type: How-to guide. Use it to find the call for one task, with its exact names and values.
Before you start
- A Taskade account with a verified email address.
- A personal access token from taskade.com/settings/api. The token starts with the
tskdp_prefix, and you can hold up to 5 tokens per account. See Personal access tokens. curlin a terminal. The longer examples use Node.js 18 or later, or Python 3 withrequests.
Save the token in your terminal. Every example on this page reads it from TASKADE_TOKEN:
export TASKADE_TOKEN="tskdp_your_token_here"
Key features
- RESTful design: standard HTTP methods and status codes
- Authentication: OAuth 2.0 and personal access tokens
- Full task CRUD: create, read, update, and delete tasks, plus assignees, dates, notes, and custom fields. Projects support create, read, complete, restore, copy, and share links
- Generated reference: the REST API reference shows every endpoint with its parameters and responses
Base URL and authentication
Base URL: https://www.taskade.com/api/v1
Authentication: Bearer token
Content-Type: application/json
Send the token in the Authorization header on every request:
curl -s https://www.taskade.com/api/v1/workspaces \
-H "Authorization: Bearer $TASKADE_TOKEN"
The response lists each workspace with its id and name:
{
"ok": true,
"items": [
{ "id": "Wk3rT9pLmQ2vXa7c", "name": "My Workspace" }
]
}
OAuth 2.0
Use OAuth 2.0 when your app acts for other Taskade users. The flow uses these two URLs:
Authorization URL: https://www.taskade.com/oauth2/authorize
Token URL: https://www.taskade.com/oauth2/token
Send the OAuth access token in the same Authorization: Bearer header. See Authentication for the full flow with PKCE.
Core API resources
The first examples in each group are full curl calls. The shorter blocks show only the method, the path, and the body.
To send one, add the base URL and the Authorization header from Base URL and authentication. The REST API reference shows the full request and response for every endpoint.
Save the IDs that you work with. The curl examples read them from these variables:
export WORKSPACE_ID="your_workspace_id"
export FOLDER_ID="your_folder_id"
export PROJECT_ID="your_project_id"
export TASK_ID="your_task_id"
Need bundle export or import? The four/bundles/*endpoints (export or import a Taskade Genesis app as a portable bundle) are documented on the Bundles & App Kits page.
Workspaces API
Manage workspaces and their contents:
Get all workspaces
curl -s https://www.taskade.com/api/v1/workspaces \
-H "Authorization: Bearer $TASKADE_TOKEN"
The response is { "ok": true, "items": [{ "id", "name" }] }. The same call in JavaScript and Python:
const response = await fetch('https://www.taskade.com/api/v1/workspaces', {
headers: { Authorization: `Bearer ${process.env.TASKADE_TOKEN}` },
});
const { items: workspaces } = await response.json();
import os
import requests
response = requests.get(
'https://www.taskade.com/api/v1/workspaces',
headers={'Authorization': f"Bearer {os.environ['TASKADE_TOKEN']}"},
)
workspaces = response.json()['items']
Get workspace folders
GET /workspaces/{workspaceId}/folders
Create a project in a workspace
Send the project as Markdown. The first # heading becomes the project title, and each list item becomes a task.
curl -s -X POST "https://www.taskade.com/api/v1/workspaces/$WORKSPACE_ID/projects" \
-H "Authorization: Bearer $TASKADE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"contentType": "text/markdown", "content": "# My New Project\n\n- Task 1\n- Task 2"}'
The response holds the new project:
{
"ok": true,
"item": { "id": "Pj8sK2nV4wQx6ZbE", "icon": null, "completed": false }
}
Folders API
Manage folders (subspaces) and their contents:
Get folder projects
GET /folders/{folderId}/projects
Get folder agents
GET /folders/{folderId}/agents
Create agent in folder
POST /folders/{folderId}/agents
Content-Type: application/json
{
"name": "Customer Support Agent",
"data": {
"type": "template",
"template": {
"type": "CustomerSupport"
}
}
}
Generate agent with AI
POST /folders/{folderId}/agent-generate
Content-Type: application/json
{
"text": "Create an agent that helps with customer support questions"
}
Get folder media files
GET /folders/{folderId}/medias
Get folder project templates
GET /folders/{folderId}/project-templates
Projects API
Project management:
GET /projects/{projectId}is read-only. v1 has noPUT,PATCH, orDELETEon a project.Project writes are separate endpoints:
/complete,/restore,/copy, and/shareLink. v1 does not rename, archive, export, or delete a project. For the project operations of v2, see the Action API guide.
Get project
GET /projects/{projectId}
Create a project in a folder
curl -s -X POST https://www.taskade.com/api/v1/projects \
-H "Authorization: Bearer $TASKADE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"folderId": "'"$FOLDER_ID"'", "contentType": "text/markdown", "content": "# Project Title\n\n- First task\n- Second task"}'
The response is { "ok": true, "item": { "id", "name", "icon", "completed" } }.
Complete project
POST /projects/{projectId}/complete
Restore project
POST /projects/{projectId}/restore
Copy project
POST /projects/{projectId}/copy
Content-Type: application/json
{
"folderId": "destination_folder_id",
"projectTitle": "Copied Project Name"
}
Create project from template
POST /projects/from-template
Content-Type: application/json
{
"folderId": "folder_123",
"templateId": "template_456"
}
Get project members
GET /projects/{projectId}/members?limit=20&page=1
Get project fields
GET /projects/{projectId}/fields
Get project share link
GET /projects/{projectId}/shareLink
Enable share link
PUT /projects/{projectId}/shareLink
Get project blocks
GET /projects/{projectId}/blocks?limit=100
Get project tasks
GET /projects/{projectId}/tasks?limit=100
Tasks API
Task management:
Get task
GET /projects/{projectId}/tasks/{taskId}
Create tasks
contentType, content, and placement are all required on each entry. Send at most 20 entries per call. Each content value is limited to 2,000 characters.
To create a task under a parent, add "taskId" and use a placement of beforebegin, afterbegin, beforeend, or afterend. Without taskId, placement must be afterbegin or beforeend.
curl -s -X POST "https://www.taskade.com/api/v1/projects/$PROJECT_ID/tasks/" \
-H "Authorization: Bearer $TASKADE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"tasks": [{"contentType": "text/markdown", "content": "New task content", "placement": "beforeend"}]}'
The response returns each task that you created, with its id:
{
"ok": true,
"item": [
{ "id": "Tk5yH1cR7mD3aF9u", "text": "New task content", "completed": false }
]
}
// Create multiple tasks. Reads the token from TASKADE_TOKEN.
const createTasks = async (projectId, tasks) => {
const response = await fetch(
`https://www.taskade.com/api/v1/projects/${projectId}/tasks/`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.TASKADE_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
tasks: tasks.map(content => ({
contentType: 'text/markdown',
content: content,
placement: 'beforeend'
}))
})
}
);
return response.json();
};
Update a task
curl -s -X PUT "https://www.taskade.com/api/v1/projects/$PROJECT_ID/tasks/$TASK_ID" \
-H "Authorization: Bearer $TASKADE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"contentType": "text/markdown", "content": "Updated task content"}'
The response is { "ok": true, "item": { "id", "text", "parentId", "completed" } }.
Delete a task
This call deletes the task from the project. Make sure that the task ID is correct before you run it.
curl -s -X DELETE "https://www.taskade.com/api/v1/projects/$PROJECT_ID/tasks/$TASK_ID" \
-H "Authorization: Bearer $TASKADE_TOKEN"
The response is { "ok": true }.
Complete a task
curl -s -X POST "https://www.taskade.com/api/v1/projects/$PROJECT_ID/tasks/$TASK_ID/complete" \
-H "Authorization: Bearer $TASKADE_TOKEN"
The response returns the task with "completed": true.
Mark task incomplete
POST /projects/{projectId}/tasks/{taskId}/uncomplete
Move task
PUT /projects/{projectId}/tasks/{taskId}/move
Content-Type: application/json
{
"target": {
"taskId": "target_task_id",
"position": "afterend"
}
}
Position options: beforebegin, afterbegin, beforeend, afterend
Get task assignees
GET /projects/{projectId}/tasks/{taskId}/assignees
Update task assignees
PUT /projects/{projectId}/tasks/{taskId}/assignees
Content-Type: application/json
{
"handles": ["user_handle_1", "user_handle_2"]
}
Remove task assignee
DELETE /projects/{projectId}/tasks/{taskId}/assignees/{assigneeHandle}
Get task date
GET /projects/{projectId}/tasks/{taskId}/date
Set task date
PUT /projects/{projectId}/tasks/{taskId}/date
Content-Type: application/json
{
"start": {
"date": "2024-12-31",
"time": "17:00:00",
"timezone": "America/New_York"
},
"end": {
"date": "2025-01-15",
"time": "17:00:00",
"timezone": "America/New_York"
}
}
For eligible workspaces with Google Calendar connected, dates set through this endpoint can follow the same calendar-sync path as dates set in Taskade.
Delete task date
DELETE /projects/{projectId}/tasks/{taskId}/date
Get task note
GET /projects/{projectId}/tasks/{taskId}/note
Update task note
PUT /projects/{projectId}/tasks/{taskId}/note
Content-Type: application/json
{
"type": "text/markdown",
"value": "This is a note for the task"
}
Delete task note
DELETE /projects/{projectId}/tasks/{taskId}/note
Get task field values
GET /projects/{projectId}/tasks/{taskId}/fields
Get specific field value
GET /projects/{projectId}/tasks/{taskId}/fields/{fieldId}
Update field value
PUT /projects/{projectId}/tasks/{taskId}/fields/{fieldId}
Content-Type: application/json
{
"value": "field_value"
}
Delete field value
DELETE /projects/{projectId}/tasks/{taskId}/fields/{fieldId}
Agents API
Manage AI agents:
Get agent
GET /agents/{agentId}
Update agent
PATCH /agents/{agentId}
Content-Type: application/json
{
"name": "Updated Agent Name"
}
If you send data, it must include the commands array. The request fails validation without it.
Delete agent
DELETE /agents/{agentId}
Enable public access
PUT /agents/{agentId}/publicAccess
Get public agent settings
GET /agents/{agentId}/public-agent
Update public agent settings
PATCH /agents/{agentId}/public-agent
Content-Type: application/json
{
"preferences": {
"mode": "chatbot",
"theme": "light",
"hideBranding": false
}
}
The update is a patch. Keys that you leave out keep their stored values, and meta merges field by field. The Action API operation updatePublicAgent follows the same rules.
If the update fails, find the error code in Fix common errors.
Get public agent by public ID
GET /public-agents/{publicAgentId}
Add project to agent knowledge
POST /agents/{agentId}/knowledge/project
Content-Type: application/json
{
"projectId": "project_123"
}
Add media to agent knowledge
POST /agents/{agentId}/knowledge/media
Content-Type: application/json
{
"mediaId": "media_456"
}
Remove project from agent knowledge
DELETE /agents/{agentId}/knowledge/project/{projectId}
Remove media from agent knowledge
DELETE /agents/{agentId}/knowledge/media/{mediaId}
Get agent conversations
GET /agents/{agentId}/convos/?limit=20&page=1
Get specific conversation
GET /agents/{agentId}/convos/{convoId}
Media API
Manage uploaded files:
Get media
GET /medias/{mediaId}
Delete media
DELETE /medias/{mediaId}
Download one media file
GET /medias/{mediaId}/download
Download a space's media
GET /medias/spaces/{spaceId}/download
Upload media to a space
Send the file as the raw request body. Pass the file name as a query parameter.
POST /medias/spaces/{spaceId}/upload?filename=report.pdf
User projects API
Access the authenticated user's projects:
Get my projects
GET /me/projects?limit=100&page=1&sort=viewed-desc
Sort options: viewed-asc, viewed-desc
Errors
HTTP status codes
- 200 OK: the request succeeded
- 400 Bad Request: invalid request parameters
- 401 Unauthorized: authentication is missing or invalid
- 402 Payment Required: the operation is not available for the current account or workspace
- 403 Forbidden: your account does not have permission
- 404 Not Found: the resource does not exist, or your account cannot see it
- 429 Too Many Requests: you hit a rate limit. See Rate limits
- 4XX: another client error. Read the response body for details
Error response format
Every error carries ok, message, code, and statusMessage. For example, a missing or wrong token returns 401:
{
"ok": false,
"message": "Unauthorized",
"code": "UNAUTHORIZED",
"statusMessage": "Unauthorized"
}
A schema validation failure returns 400 with code: "FST_ERR_VALIDATION" and the same four fields. Its message names each field that failed, then the reason.
Branch on ok and code. Do not parse the human-readable message. Treat statusMessage as a description of the HTTP status, not as a stable programmatic identifier.
Fix common errors
| What you see | Cause | Fix |
|---|---|---|
"code": "UNAUTHORIZED" |
The token is missing, wrong, or revoked | Send Authorization: Bearer $TASKADE_TOKEN. Make sure that the variable is set in the same terminal. |
"code": "FST_ERR_VALIDATION" |
A field or parameter does not match the schema | Read message, fix each field that it names, then send the request again. |
"code": "FORBIDDEN" |
Your account cannot do this in the workspace | Use an account with the correct role in that workspace. |
"code": "NOT_FOUND" |
The ID is wrong, or your account cannot see the item | Check the ID and your workspace access. |
"code": "TOO_MANY_REQUESTS" with "message": "Too Many Requests" |
You hit a rate limit | Wait for the number of seconds in the Retry-After header, then send the request again. |
"code": "PAYMENT_REQUIRED" with "message": "Your plan does not include custom agent branding." |
You set hideBranding to true on a plan without agent branding. Agent branding is on Business and above. |
Upgrade the workspace, or leave hideBranding out of the body. featureId names the missing feature. |
"code": "BAD_REQUEST" with "message": "Set the share image in the agent's publish settings." |
The body changes meta.image. The API does not accept file uploads. |
Set the share image in the agent's publish settings in the app. |
Error handling example
// Throw on every error envelope. Reads the token from TASKADE_TOKEN.
const apiRequest = async (url, options = {}) => {
const response = await fetch(url, {
...options,
headers: {
Authorization: `Bearer ${process.env.TASKADE_TOKEN}`,
'Content-Type': 'application/json',
...options.headers,
},
});
const data = await response.json();
if (!data.ok) {
throw new Error(`API Error: ${data.message} (${data.code})`);
}
return data;
};
Rate limits
Every rate-limited response carries the current budget in two sets of headers. Header names are case-insensitive:
| Header | Meaning |
|---|---|
RateLimit-Limit and X-Rate-Limit-Limit |
Your total budget for the current window |
RateLimit-Remaining and X-Rate-Limit-Remaining |
Requests left in the window (0 when you are blocked) |
RateLimit-Reset |
Whole seconds until the window reopens |
X-Rate-Limit-Reset |
Seconds until the window reopens. This value can be fractional |
Retry-After |
Sent on a 429 response only. Whole seconds to wait before you retry |
Read the headers rather than a fixed number. Taskade can change a ceiling without a docs release.
The budget for each IP address is 40 requests per 60 seconds, and it covers every public API request. The Action API v2 specification publishes this figure. Taskade does not publish the other ceilings. The headers above carry the live values:
| Scope | Counted per |
|---|---|
| Every public API request | IP address |
| Authenticated v1 endpoints | user account |
/bundles/* export and import |
IP address, on a tighter budget than ordinary reads and writes |
/public-agents/* |
IP address |
The authenticated v1 budget is keyed on the account, not on the token. Extra tokens do not add capacity. The other budgets are keyed on the IP address.
A 429 response carries Retry-After, in whole seconds. Wait that number of seconds before you retry. Earlier retries are also rejected.
const apiRequestWithRetry = async (url, options = {}, retries = 3) => {
for (let i = 0; i < retries; i++) {
const response = await fetch(url, options);
if (response.status !== 429) return response;
const resetSec = Number(
response.headers.get('retry-after') ?? response.headers.get('x-rate-limit-reset'),
);
const waitMs = resetSec > 0 ? resetSec * 1000 : 2 ** i * 1000;
await new Promise((r) => setTimeout(r, waitMs));
}
throw new Error('Rate limit retries exhausted');
};
These practices reduce 429 responses:
- Batch instead of looping. Bulk endpoints and array operations reduce the number of requests.
- Spread scheduled work. Stagger cron jobs. Do not start all jobs at
:00. - Leave rate-limit headroom for bundle transfers. The
/bundles/*budget is much tighter than the budget for regular reads and writes.
Pagination
Many endpoints support cursor pagination or page pagination:
Page-based pagination
curl -s "https://www.taskade.com/api/v1/me/projects?limit=20&page=2" \
-H "Authorization: Bearer $TASKADE_TOKEN"
Cursor-based pagination (tasks and blocks)
# Get tasks after a specific task
curl -s "https://www.taskade.com/api/v1/projects/$PROJECT_ID/tasks?limit=100&after=$TASK_ID" \
-H "Authorization: Bearer $TASKADE_TOKEN"
# Get tasks before a specific task
curl -s "https://www.taskade.com/api/v1/projects/$PROJECT_ID/tasks?limit=100&before=$TASK_ID" \
-H "Authorization: Bearer $TASKADE_TOKEN"
The tasks response has this shape:
{
"ok": true,
"items": [
{ "id": "Tk5yH1cR7mD3aF9u", "text": "New task content", "completed": false }
],
"hasMore": true,
"nextCursor": "Tk5yH1cR7mD3aF9u"
}
The tasks response also carries hasMore and nextCursor. While hasMore is true, pass nextCursor as after to fetch the next page. nextCursor is null on the last page.
Code examples
Complete workflow example
// Complete example: Create a project and add tasks
const createProjectWithTasks = async (folderId, projectName, tasks) => {
const headers = {
'Authorization': `Bearer ${process.env.TASKADE_TOKEN}`,
'Content-Type': 'application/json'
};
// Step 1: Create the project
const projectContent = `# ${projectName}\n\n` +
tasks.map(t => `- ${t}`).join('\n');
const projectResponse = await fetch(
'https://www.taskade.com/api/v1/projects',
{
method: 'POST',
headers,
body: JSON.stringify({
folderId: folderId,
contentType: 'text/markdown',
content: projectContent
})
}
);
const projectData = await projectResponse.json();
if (!projectData.ok) {
throw new Error(`Failed to create project: ${projectData.message}`);
}
return projectData.item;
};
// Usage
const project = await createProjectWithTasks(
'folder_123',
'My New Project',
['Task 1', 'Task 2', 'Task 3']
);
console.log('Created project:', project.id);
Agent integration example
// Create an agent and add knowledge
const setupAgent = async (folderId, agentName, projectIds) => {
const headers = {
'Authorization': `Bearer ${process.env.TASKADE_TOKEN}`,
'Content-Type': 'application/json'
};
// Step 1: Create the agent
const agentResponse = await fetch(
`https://www.taskade.com/api/v1/folders/${folderId}/agents`,
{
method: 'POST',
headers,
body: JSON.stringify({
name: agentName,
data: {
type: 'template',
template: {
type: 'Researcher'
}
}
})
}
);
const agentData = await agentResponse.json();
const agentId = agentData.item.id;
// Step 2: Add projects to knowledge base
for (const projectId of projectIds) {
await fetch(
`https://www.taskade.com/api/v1/agents/${agentId}/knowledge/project`,
{
method: 'POST',
headers,
body: JSON.stringify({ projectId })
}
);
}
return agentData.item;
};
Python example
import os
import requests
API_BASE = 'https://www.taskade.com/api/v1'
class TaskadeClient:
def __init__(self, token):
self.token = token
self.headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
def get_workspaces(self):
"""Get all workspaces"""
response = requests.get(f'{API_BASE}/workspaces', headers=self.headers)
return response.json()
def get_folder_projects(self, folder_id):
"""Get all projects in a folder"""
response = requests.get(
f'{API_BASE}/folders/{folder_id}/projects',
headers=self.headers
)
return response.json()
def create_project(self, folder_id, content):
"""Create a new project"""
response = requests.post(
f'{API_BASE}/projects',
headers=self.headers,
json={
'folderId': folder_id,
'contentType': 'text/markdown',
'content': content
}
)
return response.json()
def complete_task(self, project_id, task_id):
"""Mark a task as complete"""
response = requests.post(
f'{API_BASE}/projects/{project_id}/tasks/{task_id}/complete',
headers=self.headers
)
return response.json()
def get_agent(self, agent_id):
"""Get an agent"""
response = requests.get(
f'{API_BASE}/agents/{agent_id}',
headers=self.headers
)
return response.json()
# Usage
client = TaskadeClient(os.environ['TASKADE_TOKEN'])
workspaces = client.get_workspaces()
print(f"Found {len(workspaces['items'])} workspaces")
Best practices
Security
- Store API tokens in environment variables or a secret manager.
- Use HTTPS for all API calls.
- Handle API errors.
- Never expose tokens in client-side code.
Performance
- Use pagination for large result sets.
- Cache responses when the data permits caching.
- Use exponential backoff for retries.
- Batch operations when an endpoint supports batching.
Data management
- Validate data before you send it to the API.
- Record and retry eligible partial failures.
- Add logging and monitoring.
Note: For API integration or enterprise API requirements, email support@taskade.com.
Next steps
- REST API reference: every v1 endpoint, with its parameters and responses.
- Quickstart: create a project and add tasks in about five minutes.
- Action API guide: the v2 API, with
promptAgentand signed webhooks. - Authentication: OAuth 2.0 for apps that act for other users.
- Bundles and App Kits: export and import a Taskade Genesis app.