Getting Started

REST API Guide

16 min readStart hereView as Markdown
Getting Started

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.
  • curl in a terminal. The longer examples use Node.js 18 or later, or Python 3 with requests.

Save the token in your terminal. Every example on this page reads it from TASKADE_TOKEN:

Bash
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:

Bash
curl -s https://www.taskade.com/api/v1/workspaces \
  -H "Authorization: Bearer $TASKADE_TOKEN"

The response lists each workspace with its id and name:

Json
{
  "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:

Bash
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

Bash
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:

Javascript
const response = await fetch('https://www.taskade.com/api/v1/workspaces', {
  headers: { Authorization: `Bearer ${process.env.TASKADE_TOKEN}` },
});
const { items: workspaces } = await response.json();
Python
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

Http
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.

Bash
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:

Json
{
  "ok": true,
  "item": { "id": "Pj8sK2nV4wQx6ZbE", "icon": null, "completed": false }
}

Folders API

Manage folders (subspaces) and their contents:

Get folder projects

Http
GET /folders/{folderId}/projects

Get folder agents

Http
GET /folders/{folderId}/agents

Create agent in folder

Http
POST /folders/{folderId}/agents
Content-Type: application/json

{
  "name": "Customer Support Agent",
  "data": {
    "type": "template",
    "template": {
      "type": "CustomerSupport"
    }
  }
}

Generate agent with AI

Http
POST /folders/{folderId}/agent-generate
Content-Type: application/json

{
  "text": "Create an agent that helps with customer support questions"
}

Get folder media files

Http
GET /folders/{folderId}/medias

Get folder project templates

Http
GET /folders/{folderId}/project-templates

Projects API

Project management:


GET /projects/{projectId} is read-only. v1 has no PUT, PATCH, or DELETE on 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

Http
GET /projects/{projectId}

Create a project in a folder

Bash
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

Http
POST /projects/{projectId}/complete

Restore project

Http
POST /projects/{projectId}/restore

Copy project

Http
POST /projects/{projectId}/copy
Content-Type: application/json

{
  "folderId": "destination_folder_id",
  "projectTitle": "Copied Project Name"
}

Create project from template

Http
POST /projects/from-template
Content-Type: application/json

{
  "folderId": "folder_123",
  "templateId": "template_456"
}

Get project members

Http
GET /projects/{projectId}/members?limit=20&page=1

Get project fields

Http
GET /projects/{projectId}/fields
Http
GET /projects/{projectId}/shareLink
Http
PUT /projects/{projectId}/shareLink

Get project blocks

Http
GET /projects/{projectId}/blocks?limit=100

Get project tasks

Http
GET /projects/{projectId}/tasks?limit=100

Tasks API

Task management:

Get task

Http
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.

Bash
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:

Json
{
  "ok": true,
  "item": [
    { "id": "Tk5yH1cR7mD3aF9u", "text": "New task content", "completed": false }
  ]
}
Javascript
// 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

Bash
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.

Bash
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

Bash
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

Http
POST /projects/{projectId}/tasks/{taskId}/uncomplete

Move task

Http
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

Http
GET /projects/{projectId}/tasks/{taskId}/assignees

Update task assignees

Http
PUT /projects/{projectId}/tasks/{taskId}/assignees
Content-Type: application/json

{
  "handles": ["user_handle_1", "user_handle_2"]
}

Remove task assignee

Http
DELETE /projects/{projectId}/tasks/{taskId}/assignees/{assigneeHandle}

Get task date

Http
GET /projects/{projectId}/tasks/{taskId}/date

Set task date

Http
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

Http
DELETE /projects/{projectId}/tasks/{taskId}/date

Get task note

Http
GET /projects/{projectId}/tasks/{taskId}/note

Update task note

Http
PUT /projects/{projectId}/tasks/{taskId}/note
Content-Type: application/json

{
  "type": "text/markdown",
  "value": "This is a note for the task"
}

Delete task note

Http
DELETE /projects/{projectId}/tasks/{taskId}/note

Get task field values

Http
GET /projects/{projectId}/tasks/{taskId}/fields

Get specific field value

Http
GET /projects/{projectId}/tasks/{taskId}/fields/{fieldId}

Update field value

Http
PUT /projects/{projectId}/tasks/{taskId}/fields/{fieldId}
Content-Type: application/json

{
  "value": "field_value"
}

Delete field value

Http
DELETE /projects/{projectId}/tasks/{taskId}/fields/{fieldId}

Agents API

Manage AI agents:

Get agent

Http
GET /agents/{agentId}

Update agent

Http
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

Http
DELETE /agents/{agentId}

Enable public access

Http
PUT /agents/{agentId}/publicAccess

Get public agent settings

Http
GET /agents/{agentId}/public-agent

Update public agent settings

Http
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

Http
GET /public-agents/{publicAgentId}

Add project to agent knowledge

Http
POST /agents/{agentId}/knowledge/project
Content-Type: application/json

{
  "projectId": "project_123"
}

Add media to agent knowledge

Http
POST /agents/{agentId}/knowledge/media
Content-Type: application/json

{
  "mediaId": "media_456"
}

Remove project from agent knowledge

Http
DELETE /agents/{agentId}/knowledge/project/{projectId}

Remove media from agent knowledge

Http
DELETE /agents/{agentId}/knowledge/media/{mediaId}

Get agent conversations

Http
GET /agents/{agentId}/convos/?limit=20&page=1

Get specific conversation

Http
GET /agents/{agentId}/convos/{convoId}

Media API

Manage uploaded files:

Get media

Http
GET /medias/{mediaId}

Delete media

Http
DELETE /medias/{mediaId}

Download one media file

Http
GET /medias/{mediaId}/download

Download a space's media

Http
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.

Http
POST /medias/spaces/{spaceId}/upload?filename=report.pdf

User projects API

Access the authenticated user's projects:

Get my projects

Http
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:

Json
{
  "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

Javascript
// 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.

Javascript
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

Bash
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)

Bash
# 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:

Json
{
  "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

Javascript
// 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

Javascript
// 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

Python
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