A reference guide for building AI agents: every method, how to authenticate, and the permissions each one needs.
The Asana API is how an app or AI agent works with an Asana workspace: creating and searching tasks, organizing them into projects and sections, posting comments as stories, and reading the people and teams in a workspace. Access is granted through a Bearer token, either an OAuth access token carrying granular scopes in a resource:action shape, like tasks:write, or a personal access token that holds a user's full permissions. The API runs at a single continuously updated version, and a change in the workspace can be pushed to a registered webhook as an event.
How an app or AI agent connects to Asana determines what it can reach. There is a REST API for making calls, a webhook channel for receiving events, and a hosted server that exposes Asana tools to agents, and each is governed by the token behind it and the permissions that token carries.
The REST API answers at https://app.asana.com/api/1.0. It takes and returns JSON, wraps every payload in a data envelope, and pages through lists with an opaque cursor. A call authenticates with a Bearer token, either an OAuth access token or a personal access token. There is no dated version to pin; the API at 1.0 is continuously updated.
A hosted Model Context Protocol server at https://mcp.asana.com/v2/mcp exposes Asana's Work Graph to AI agents and LLM clients over streamable HTTP. It went generally available in 2026 and authenticates with OAuth, so a connecting client is prompted to authorize access to the user's Asana data. Its tools cover creating and searching tasks, project tracking, and status updates, and the live set is discoverable with the tools/list command.
Asana POSTs events to a target URL registered through the Webhooks API. Setup is a two-part handshake: Asana sends an X-Hook-Secret header that the target echoes back, then signs every later delivery with an X-Hook-Signature HMAC computed from that secret, so the receiver can confirm the request came from Asana. Events report an action and the changed resource, and an app fetches the detail through the REST API.
OAuth 2.0 is the recommended way for an app to act on behalf of an Asana user. Since granular scopes shipped, an app requests only the resource and action pairs it needs, like tasks:read or projects:write, rather than full account access. A registered app exchanges an authorization code for an access token and a refresh token, and the token carries exactly the scopes the user consented to.
A personal access token is a credential a user generates for their own account, sent as a Bearer token. It carries the full permissions of the user who created it and is not scoped, so it suits scripts and a single user's own integrations rather than multi-user apps. It should be guarded like a password.
A service account is an organization-owned token available on Enterprise and Enterprise+ plans, not tied to a person, used for server-to-server automation and admin-only APIs like SCIM user provisioning, audit logs, and exports. It does not consume a paid seat and is managed by a workspace admin.
An app registered with Full permissions can request the default scope, which grants full access to everything the consenting user can reach, the all-or-nothing model that predates granular scopes. An app registered with specific scopes must request them explicitly instead, or a default request returns a forbidden_scopes error.
The Asana API is split into areas an agent can act on, like tasks, projects, sections, stories, users, and webhooks. Each area has its own methods, and each granular OAuth scope grants only one resource and one action, so a token reads or writes only what it was scoped to.
List, read, create, update, delete, and search tasks, the core unit of work in Asana.
List, read, create, and update projects, and list the tasks within a project.
List the sections in a project and create a new section to group tasks.
List the stories on a task and post a comment, where a story is a comment or an activity-log entry.
Read the authenticated user, read another user by id, and list the users in a workspace.
List the workspaces a token can reach, read one, and list the teams in a workspace.
List a workspace's webhooks, establish a new one, read one, and delete one.
Filter by method, access, or permission, or search any path. Select a row for version detail, rate limits, the related webhook event, and the source.
| Method | Endpoint | What it does | Access | Permission | Version | |
|---|---|---|---|---|---|---|
TasksList, read, create, update, delete, and search tasks, the core unit of work in Asana.6 | ||||||
| GET | /tasks | Get multiple tasks, filtered by project, section, assignee, or workspace. | read | tasks:read | Current | |
Returns compact task objects. A filter such as project, section, assignee with workspace, or tag is required. Acts ontask Permission (capability) tasks:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /tasks/{task_gid} | Get the complete record for a single task. | read | tasks:read | Current | |
Read-only. Acts ontask Permission (capability) tasks:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /tasks | Create a task in a workspace, optionally placing it in projects or under a parent task. | write | tasks:write | Current | |
A task's workspace is fixed once set, and can be implied by passing projects or a parent. Acts ontask Permission (capability) tasks:writeVersionAvailable since the API’s base version Webhook event addedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PUT | /tasks/{task_gid} | Update a task's fields, like its name, notes, assignee, due date, or completed state. | write | tasks:write | Current | |
Only the fields included in the request are changed. Acts ontask Permission (capability) tasks:writeVersionAvailable since the API’s base version Webhook event changedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /tasks/{task_gid} | Delete a task, moving it to the trash for 30 days before permanent removal. | write | tasks:delete | Current | |
Needs the separate tasks:delete scope, not tasks:write. The task sits in the trash for 30 days and can be undeleted in that window. Acts ontask Permission (capability) tasks:deleteVersionAvailable since the API’s base version Webhook event deletedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /workspaces/{workspace_gid}/tasks/search | Search tasks in a workspace with rich filters on text, assignee, dates, and custom fields. | read | tasks:read | Current | |
The Search API has its own limit of 60 requests per minute, separate from the standard rate limit. Acts ontask Permission (capability) tasks:readVersionAvailable since the API’s base version Webhook eventNone Rate limit60 requests per minute SourceOfficial documentation ↗ | ||||||
ProjectsList, read, create, and update projects, and list the tasks within a project.5 | ||||||
| GET | /projects | Get multiple projects, filtered by workspace, team, or archived state. | read | projects:read | Current | |
Read-only. Acts onproject Permission (capability) projects:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /projects/{project_gid} | Get the complete record for a single project. | read | projects:read | Current | |
Read-only. Acts onproject Permission (capability) projects:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /projects | Create a project in a workspace or team. | write | projects:write | Current | |
A project must be created in a workspace, and in an organization it must also name a team. Acts onproject Permission (capability) projects:writeVersionAvailable since the API’s base version Webhook event addedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PUT | /projects/{project_gid} | Update a project's fields, like its name, notes, color, or archived state. | write | projects:write | Current | |
Only the fields included in the request are changed. Acts onproject Permission (capability) projects:writeVersionAvailable since the API’s base version Webhook event changedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /projects/{project_gid}/tasks | Get the tasks in a project, ordered by their priority within the project. | read | tasks:read | Current | |
Reads tasks, so it needs tasks:read rather than projects:read. Acts ontask Permission (capability) tasks:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
SectionsList the sections in a project and create a new section to group tasks.2 | ||||||
| GET | /projects/{project_gid}/sections | Get the sections in a project, the columns or groupings that hold tasks. | read | projects:read | Current | |
Sections have no scope of their own and use the projects scopes. Acts onsection Permission (capability) projects:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /projects/{project_gid}/sections | Create a section in a project to group tasks. | write | projects:write | Current | |
Sections have no scope of their own and use the projects scopes. Acts onsection Permission (capability) projects:writeVersionAvailable since the API’s base version Webhook event addedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Stories (comments)List the stories on a task and post a comment, where a story is a comment or an activity-log entry.2 | ||||||
| GET | /tasks/{task_gid}/stories | Get the stories on a task, where a story is a comment or an activity-log entry. | read | stories:read | Current | |
Stories include both human comments and system-generated activity entries. Acts onstory Permission (capability) stories:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /tasks/{task_gid}/stories | Post a comment on a task by creating a story. | write | stories:write | Current | |
Only comment stories can be created; activity stories are generated by Asana. Acts onstory Permission (capability) stories:writeVersionAvailable since the API’s base version Webhook event addedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
UsersRead the authenticated user, read another user by id, and list the users in a workspace.3 | ||||||
| GET | /users/me | Get the full record for the user the token authenticates as. | read | users:read | Current | |
A common first call to confirm a token works and learn the caller's workspaces. Acts onuser Permission (capability) users:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /users/{user_gid} | Get the record for a single user by id, email, or the keyword me. | read | users:read | Current | |
Returns email, name, and photo for a user the caller shares a workspace with. Acts onuser Permission (capability) users:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /users | Get multiple users, filtered by workspace or team. | read | users:read | Current | |
A workspace or team filter is required. Acts onuser Permission (capability) users:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Workspaces & teamsList the workspaces a token can reach, read one, and list the teams in a workspace.3 | ||||||
| GET | /workspaces | Get the workspaces and organizations the token can reach. | read | workspaces:read | Current | |
Read-only. Acts onworkspace Permission (capability) workspaces:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /workspaces/{workspace_gid} | Get the full record for a single workspace or organization. | read | workspaces:read | Current | |
Read-only. Acts onworkspace Permission (capability) workspaces:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /workspaces/{workspace_gid}/teams | Get the teams in a workspace or organization. | read | teams:read | Current | |
Returns the teams visible to the authenticated user. Acts onteam Permission (capability) teams:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
WebhooksList a workspace's webhooks, establish a new one, read one, and delete one.4 | ||||||
| GET | /webhooks | Get the webhooks an app has established in a workspace. | read | webhooks:read | Current | |
A workspace filter is required. Acts onwebhook Permission (capability) webhooks:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /webhooks | Establish a webhook on a resource, completing the X-Hook-Secret handshake. | write | webhooks:write | Current | |
Asana POSTs an X-Hook-Secret to the target during this call, and the target must echo it back to confirm the subscription. Acts onwebhook Permission (capability) webhooks:writeVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /webhooks/{webhook_gid} | Get the record for a single webhook, including its filters and delivery status. | read | webhooks:read | Current | |
Read-only. Acts onwebhook Permission (capability) webhooks:readVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /webhooks/{webhook_gid} | Delete a webhook so Asana stops sending its events. | write | webhooks:delete | Current | |
Needs the separate webhooks:delete scope, not webhooks:write. Acts onwebhook Permission (capability) webhooks:deleteVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Asana can notify an app when something changes in a workspace, like a task being added or a comment being posted, instead of the app polling for updates. It POSTs events to a registered target URL, and an app verifies each delivery against a shared secret agreed during the setup handshake.
| Event | What it signals | Triggered by |
|---|---|---|
added | Fires when a resource is created or added to a parent the webhook watches, like a task added to a project or a comment added to a task. | /tasks/projects/projects/{project_gid}/sections/tasks/{task_gid}/stories |
changed | Fires when a field on a watched resource is modified, like a task's assignee, due date, or completed state changing. | /tasks/{task_gid}/projects/{project_gid} |
removed | Fires when a resource is removed from a parent without being deleted, like a task removed from a project it still otherwise exists in. | In-app only |
deleted | Fires when a watched resource is itself deleted and moved to the trash. | /tasks/{task_gid} |
undeleted | Fires when a previously deleted resource is restored from the trash within its retention window. | In-app only |
Asana limits how fast an app can call, by a per-token request rate measured per minute, by a separate cap on concurrent requests, and by a cost quota that charges expensive reads more heavily.
Asana meters requests per authorization token, so each token has its own quota and one token's traffic does not affect another. A standard per-minute request limit applies, set to 150 requests per minute for a free domain and 1,500 per minute for a paid domain (Starter through Enterprise+). The Search API is held separately to 60 requests per minute. On top of the request rate, a concurrency cap allows at most 50 GET requests and 15 POST, PUT, PATCH, or DELETE requests in flight at once, and duplication, instantiation, and export jobs are limited to 5 concurrent jobs per user. A cost-based quota also charges expensive reads more, deducting from an allowance after each response is built, based on how much of the work graph the request traversed. Exceeding any of these returns HTTP 429 with a Retry-After header giving the seconds to wait.
List endpoints are cursor-paginated. A request sets limit to choose the page size, between 1 and 100, and the response returns a next_page object holding an offset cursor. The offset is passed back on the following request to fetch the next page, and the SDKs can iterate pages automatically. The offset is opaque and must come from a prior response rather than being constructed by hand.
A list page returns at most 100 objects, the maximum value of limit. A deleted task or other resource stays in the trash and can be undeleted for 30 days before permanent removal. Responses are JSON wrapped in a top-level data field, with optional fields selected through the opt_fields parameter to keep payloads small.
The status codes an agent should handle, and what to do about each.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | Bad Request | The request was malformed, usually a missing or invalid parameter. The errors array names the problem. | Read the error message, fix the parameters, and resend. The request is not retryable as-is. |
| 401 | Unauthorized | No valid authentication token was provided with the request. | Send a valid Bearer token, and refresh an expired OAuth access token using its refresh token. |
| 402 | Payment Required | The requested object or feature is only available to premium or paid organizations. | Upgrade the organization's plan, or avoid the premium-only feature. |
| 403 | Forbidden | The token is valid but lacks permission for this request, for example a scope it was not granted or a resource the user cannot reach. | Grant the missing OAuth scope, or confirm the user has access to the resource. |
| 404 | Not Found | The object the request names does not exist, or is not visible to this token. | Verify the object id, and confirm the token has access to it. |
| 429 | Too Many Requests | A rate limit was exceeded, either the per-minute request rate, the concurrent-request cap, or the cost quota. | Wait the number of seconds in the Retry-After header, then retry, and smooth the request rate. |
| 451 | Unavailable For Legal Reasons | The request was blocked for legal reasons, often an embargoed IP address. | The request cannot proceed from this location; contact Asana support if this is unexpected. |
| 500 | Internal Server Error | A problem on Asana's end. The response includes a unique phrase that support can use to find the incident. | Retry with backoff, and quote the phrase to Asana support if it persists. |
Asana runs a single, continuously updated API at version 1.0 with no dated version to pin, and ships dated changes through its developer changelog, flagging deprecations with a warning header.
Asana runs a single API at version 1.0 with no dated version to pin and no version header. Changes ship continuously and are announced in the developer changelog, with deprecations flagged by an Asana-Deprecation-Warning header during a transition window. The dated entries below are notable changes from that changelog, newest first.
Asana moved from an all-or-nothing OAuth model to granular scopes in the resource:action shape, so an app can request only the resource and action pairs it needs, like tasks:read or projects:write, including separate delete scopes and webhook scopes. Apps already registered with Full permissions continued to use the legacy default scope until self-serve granular scopes were rolled out to them.
There is no version to pin; track the changelog for deprecations and new fields.
Asana API changelog ↗Bollard AI sits between a team's AI agents and Asana. Grant each agent exactly the access it needs, read or write, resource by resource, and every call is checked and logged.