Getting Started

Taskade API Authentication

9 min readStart hereView as Markdown

Send a Bearer token in the Authorization header on every request. REST API v1, Action API v2, and the MCP servers all accept it.

Page type: How-to guide. Use it when you know the goal and need the steps.

TL;DR: To act as yourself, create a personal access token at taskade.com/settings/api and send Authorization: Bearer $TASKADE_TOKEN. To act for other users, use OAuth 2.0 with PKCE. An access token lasts 1 hour, and each refresh returns a new refresh token.

Before you start

  • A Taskade account with a verified email address. Taskade does not create a token or an OAuth application for an unverified account.
  • curl in a terminal. The PKCE example also uses openssl.
  • For OAuth 2.0, a redirect URI that your app controls.

Choose a credential

If you are… Use How
Writing a script, or running the local Workspace MCP server (@taskade/mcp-server) Personal access token Authorization: Bearer tskdp_…
Building a third-party app that acts on behalf of other users OAuth 2.0 (Authorization Code + PKCE) Redirect flow
Connecting an interactive MCP client to the hosted server at https://www.taskade.com/mcp OAuth 2.0 Best default. The MCP client handles sign-in and consent
Calling the hosted MCP from your own script, service, or client that can set headers Personal access token Send Authorization: Bearer tskdp_… directly

Personal access tokens

Personal access tokens authenticate requests as yourself. They work well for server scripts, the local Workspace MCP server, and early development.

Create a token

  1. Go to taskade.com/settings/api.
  2. Go to Personal access tokens and click Create token.
  3. Give the token a descriptive name.
  4. Copy the token immediately. Taskade shows it only once.
  5. Save the token in your terminal:
Bash
export TASKADE_TOKEN="tskdp_your_token_here"

A personal access token starts with the prefix tskdp_. You can hold up to 5 tokens per account, and your email must be verified to create one.


A personal access token grants full access to your account. Treat it like a password.

Never commit or share the token. Store it in an environment variable or secret manager.

Use a token

Send it in the Authorization header on every request:

Bash
# REST API v1
curl -s https://www.taskade.com/api/v1/me/projects \
  -H "Authorization: Bearer $TASKADE_TOKEN"

# Action API v2
curl -s -X POST https://www.taskade.com/api/v2/listMyProjects \
  -H "Authorization: Bearer $TASKADE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'

A valid token returns ok: true and the projects that your account can open:

Json
{ "ok": true, "items": [{ "id": "Pj8sK2nV4wQx6ZbE", "name": "API quickstart" }] }

A missing, wrong, or revoked token returns 401:

Json
{ "ok": false, "message": "Unauthorized", "code": "UNAUTHORIZED", "statusMessage": "Unauthorized" }

The local Workspace MCP server (@taskade/mcp-server) reads the token from the TASKADE_API_KEY environment variable, not from TASKADE_TOKEN. See Workspace MCP.

Use a personal token with hosted MCP

The hosted endpoint at https://www.taskade.com/mcp accepts the same tskdp_… personal access token as the public APIs:

Http
POST https://www.taskade.com/mcp
Authorization: Bearer tskdp_your_token_here
Content-Type: application/json

Choose this option for a script, server process, CI job, or MCP client where you control the request headers.

The connection acts as your Taskade account. Personal tokens carry no OAuth scopes and grant their owner's access. Do not distribute them.

For Claude, Cursor, and other interactive clients with remote MCP OAuth support, prefer the OAuth flow. The flow keeps a full-account personal token out of the client. It also gives each user a separate consent flow.


OAuth 2.0

Use OAuth 2.0 when your application acts on behalf of other users. Each user signs in and grants access.

Also use OAuth 2.0 for interactive clients that connect to hosted MCP. Header-controlled scripts can use a personal access token instead.

Endpoints

Purpose URL
Authorization https://www.taskade.com/oauth2/authorize
Token / Refresh https://www.taskade.com/oauth2/token
Dynamic client registration https://www.taskade.com/oauth2/register
Server metadata (RFC 8414) https://www.taskade.com/.well-known/oauth-authorization-server

Register an application

Go to taskade.com/settings/api → OAuth2 Applications. Click Create application, then register your app and set its redirect URI(s). Your email must be verified before you can create an application.

Taskade gives you a Client ID and Client Secret. Save them, and the redirect URI, in your terminal:

Bash
export CLIENT_ID="your_client_id"
export CLIENT_SECRET="your_client_secret"
export REDIRECT_URI="https://your-app.example.com/callback"

Authorization Code flow (with PKCE)

Taskade supports the Authorization Code grant with PKCE (S256), the recommended flow for both confidential and public clients.

  1. Redirect the user to /oauth2/authorize with client_id, redirect_uri, response_type=code, code_challenge, and code_challenge_method=S256. If the token must reach the hosted MCP server, add scope=mcp. Taskade supports no other scope, and the REST and Action APIs ignore scope.
  2. Exchange the returned code at /oauth2/token with grant_type=authorization_code and your code_verifier.
    • For confidential apps, also send client_secret.
    • For public clients, omit client_secret.
  3. Store the access_token and refresh_token. The access token is valid for 1 hour.

Generate the PKCE pair. Most implementation errors occur in this step:

Bash
# code_verifier: a high-entropy random string
code_verifier=$(openssl rand -base64 96 | tr -d '\n=+/' | cut -c1-64)

# code_challenge = BASE64URL( SHA256(code_verifier) )
code_challenge=$(printf '%s' "$code_verifier" \
  | openssl dgst -binary -sha256 \
  | openssl base64 | tr -d '=\n' | tr '+/' '-_')

Send code_challenge (with code_challenge_method=S256) on the authorize request. Then send the original code_verifier on the token exchange that follows.

Exchange the code for tokens

Your redirect URI receives the authorization code in the code query parameter. Save it, then exchange it in the same terminal that holds code_verifier:

Bash
export AUTH_CODE="code_from_the_redirect"

curl -s -X POST https://www.taskade.com/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=$AUTH_CODE" \
  -d "code_verifier=$code_verifier" \
  -d "client_id=$CLIENT_ID" \
  -d "client_secret=$CLIENT_SECRET" \
  -d "redirect_uri=$REDIRECT_URI"

A public client omits the client_secret line. The response carries both tokens:

Json
{
  "access_token": "eyJhbGciOi…",
  "refresh_token": "…",
  "expires_in": 3600,
  "token_type": "Bearer"
}

Send access_token as Authorization: Bearer <access_token> on each API or MCP request.

Refresh an expired token

Access tokens expire after one hour. Exchange the stored refresh token for a new pair:

Bash
export REFRESH_TOKEN="your_stored_refresh_token"

curl -s -X POST https://www.taskade.com/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=$REFRESH_TOKEN" \
  -d "client_id=$CLIENT_ID" \
  -d "client_secret=$CLIENT_SECRET"

Every refresh request must send client_id. A confidential app must also send client_secret, the same as on the code exchange. A public client omits the client_secret line.

The response has the same shape as the code exchange. It carries a new refresh_token, and the old refresh token stops working:

Json
{
  "access_token": "eyJhbGciOi…",
  "refresh_token": "a_new_refresh_token",
  "expires_in": 3600,
  "token_type": "Bearer"
}

Store the new refresh token before you send the next request. Each refresh token works once:


Taskade supports the authorization_code and refresh_token grants only. Taskade rejects password, implicit, and client_credentials.

Scopes and the hosted MCP

When you use OAuth, the hosted MCP server at https://www.taskade.com/mcp requires an access token with the mcp scope. When you add the server URL, MCP clients (Claude Desktop, Cursor, Claude Code) do this handshake automatically, with dynamic client registration.

Personal access tokens do not use OAuth scopes. If you set the Authorization header yourself, as shown earlier, use a personal token.


Fix common errors

The token endpoint returns OAuth errors with status 400, in the RFC 6749 shape. For example, an expired or reused code returns:

Json
{ "error": "invalid_grant", "error_description": "Grant not found" }
What you see Cause Fix
"code": "UNAUTHORIZED" on an API call The token is missing, wrong, revoked, or expired Send Authorization: Bearer $TASKADE_TOKEN, or refresh the OAuth access token.
Cannot create more than 5 personal access tokens. An account holds 5 tokens maximum Revoke a token that you do not use.
"error_description": "Grant not found" The code is wrong, expired, already used, or sent with a different redirect_uri Start the authorize step again. Send the same redirect_uri on both steps.
"error_description": "Missing code_verifier" The token request has no code_verifier Send the verifier that produced the code_challenge.
"error_description": "Invalid code_verifier" The verifier does not match the code_challenge Keep the verifier from the authorize step, and send it without changes.
Invalid code_challenge_method on the authorize request The authorize request sent a code_challenge with a method other than S256 Send code_challenge_method=S256.
"error_description": "Invalid client credentials" A confidential app sent no client_secret, or a wrong one, on the code exchange or the refresh Send the client_secret of your OAuth app on both token requests.
"code": "UNAUTHORIZED" after the user reset the password A password reset revokes every OAuth grant, access token, and personal access token of the account Send the user through the authorize step again.
"error_description": "Refresh token not found" The refresh token is wrong, or a newer one replaced it Use the most recent refresh_token. If you lost it, send the user through the authorize step again.

Security best practices

  • Never expose tokens in client-side code. Personal tokens grant full account access.
  • Use OAuth, not personal tokens, for an app that more than one person uses.
  • Store refresh tokens encrypted at rest. They are long-lived.
  • Rotate personal tokens periodically. You can keep up to 5 active.
  • Always use HTTPS.

Next steps