Summary and recommendation
Asana's REST API (base URL: `https://app.asana.com/api/1.0`) supports OAuth 2.0 and Personal Access Tokens for auth. The `default` scope covers all standard user read/write operations. List endpoints return compact objects by default - always append `?opt_fields=name,email` or required fields will be silently omitted.
Pagination uses an opaque offset token, not page numbers; follow `next_page.offset` in each response until `next_page` is null. Rate limits are 150 req/min on free plans and 1,500 req/min on Starter through Enterprise+; HTTP 429 responses include a `Retry-After` header - implement exponential backoff.
For teams managing Asana alongside a broader identity stack, Stitchflow's MCP server with ~100 deep IT/identity integrations handles cross-app user lifecycle orchestration without requiring custom glue code per integration.
API quick reference
| Has user API | Yes |
| Auth method | OAuth 2.0 or Personal Access Token (PAT) |
| Base URL | Official docs |
| SCIM available | Yes |
| SCIM plan required | Enterprise |
Authentication
Auth method: OAuth 2.0 or Personal Access Token (PAT)
Setup steps
- Register an application at https://app.asana.com/0/developer-console to obtain a client_id and client_secret.
- For OAuth 2.0: redirect users to https://app.asana.com/-/oauth_authorize with response_type=code, client_id, redirect_uri, and scope.
- Exchange the returned authorization code for an access token at https://app.asana.com/-/oauth_token.
- For PAT: generate a token in My Profile Settings > Apps > Manage Developer Apps and pass it as a Bearer token in the Authorization header.
- Include the token in all requests: Authorization: Bearer {token}.
Required scopes
| Scope | Description | Required for |
|---|---|---|
| default | Grants access to all standard API endpoints on behalf of the user, including reading and writing tasks, projects, and user data. | All general API operations including user reads |
| openid | OpenID Connect scope for identity verification. | OIDC-based authentication flows |
| Access to the authenticated user's email address. | Retrieving user email via OIDC | |
| profile | Access to the authenticated user's name and profile photo. | Retrieving user profile info via OIDC |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| gid | string | Globally unique identifier for the user. | system-generated | immutable | Use this as the primary key for all user references. |
| resource_type | string | Always 'user' for user objects. | system-generated | immutable | |
| name | string | Full name of the user. | required | updatable | Read-only via REST API for other users; users can update their own name. |
| string | Primary email address of the user. | required | read-only via REST | Email changes must be done by the user or via SCIM. | |
| photo | object | Map of photo sizes (image_21x21, image_27x27, image_36x36, image_60x60, image_128x128) to URLs. | optional | updatable | |
| workspaces | array | List of workspace/organization objects the user belongs to. | system-managed | managed via membership endpoints | Each entry contains gid, name, resource_type. |
| teams | array | Teams the user is a member of (returned via team membership endpoints). | managed via team endpoints | managed via team endpoints | Not returned on the base user object; requires separate call. |
Core endpoints
Get current user
- Method: GET
- URL:
https://app.asana.com/api/1.0/users/me - Watch out for: Returns data for the authenticated token's user only. Requires no additional scopes beyond 'default'.
Request example
GET /api/1.0/users/me
Authorization: Bearer {token}
Response example
{
"data": {
"gid": "12345",
"resource_type": "user",
"name": "Jane Doe",
"email": "jane@example.com"
}
}
Get a user by GID
- Method: GET
- URL:
https://app.asana.com/api/1.0/users/{user_gid} - Watch out for: You can also use the user's email address as the {user_gid} path parameter.
Request example
GET /api/1.0/users/12345
Authorization: Bearer {token}
Response example
{
"data": {
"gid": "12345",
"name": "Jane Doe",
"email": "jane@example.com",
"workspaces": [{"gid": "99", "name": "Acme Corp"}]
}
}
List users in a workspace
- Method: GET
- URL:
https://app.asana.com/api/1.0/workspaces/{workspace_gid}/users - Watch out for: Returns compact user objects by default. Use ?opt_fields=email,name,photo to expand fields.
Request example
GET /api/1.0/workspaces/99/users?limit=50
Authorization: Bearer {token}
Response example
{
"data": [
{"gid": "12345", "name": "Jane Doe", "resource_type": "user"}
],
"next_page": {"offset": "abc123", "path": "/workspaces/99/users?offset=abc123"}
}
List users in a team
- Method: GET
- URL:
https://app.asana.com/api/1.0/teams/{team_gid}/users - Watch out for: Team GID must be known in advance; use GET /organizations/{org_gid}/teams to list teams first.
Request example
GET /api/1.0/teams/77/users
Authorization: Bearer {token}
Response example
{
"data": [
{"gid": "12345", "name": "Jane Doe", "resource_type": "user"}
]
}
Add user to a team
- Method: POST
- URL:
https://app.asana.com/api/1.0/teams/{team_gid}/addUser - Watch out for: User must already be a member of the workspace/organization. Adding to a team does not add to the workspace.
Request example
POST /api/1.0/teams/77/addUser
Content-Type: application/json
{"data": {"user": "12345"}}
Response example
{
"data": {
"gid": "12345",
"name": "Jane Doe",
"resource_type": "user"
}
}
Remove user from a team
- Method: POST
- URL:
https://app.asana.com/api/1.0/teams/{team_gid}/removeUser - Watch out for: Returns an empty data object on success. Does not remove the user from the workspace.
Request example
POST /api/1.0/teams/77/removeUser
Content-Type: application/json
{"data": {"user": "12345"}}
Response example
{
"data": {}
}
Add user to a workspace
- Method: POST
- URL:
https://app.asana.com/api/1.0/workspaces/{workspace_gid}/addUser - Watch out for: For organizations (not free workspaces), this sends an invitation email. The user is not fully provisioned until they accept.
Request example
POST /api/1.0/workspaces/99/addUser
Content-Type: application/json
{"data": {"user": "jane@example.com"}}
Response example
{
"data": {
"gid": "12345",
"name": "Jane Doe",
"email": "jane@example.com"
}
}
Remove user from a workspace
- Method: POST
- URL:
https://app.asana.com/api/1.0/workspaces/{workspace_gid}/removeUser - Watch out for: This deactivates the user from the workspace. Tasks assigned to them remain but become unassigned or stay assigned depending on workspace settings.
Request example
POST /api/1.0/workspaces/99/removeUser
Content-Type: application/json
{"data": {"user": "12345"}}
Response example
{
"data": {}
}
Rate limits, pagination, and events
- Rate limits: Asana enforces rate limits per API key/token. The standard limit is 1,500 requests per minute for most plans. Premium/Business/Enterprise may have higher limits. Concurrent request limits also apply.
- Rate-limit headers: Yes
- Retry-After header: Yes
- Rate-limit notes: When rate limited, Asana returns HTTP 429. The Retry-After header indicates when to retry. Response headers include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.
- Pagination method: offset
- Default page size: 20
- Max page size: 100
- Pagination pointer: offset
| Plan | Limit | Concurrent |
|---|---|---|
| Free / Personal | 150 requests/minute | 0 |
| Starter / Advanced | 1500 requests/minute | 0 |
| Enterprise / Enterprise+ | 1500 requests/minute (higher limits available on request) | 0 |
- Webhooks available: Yes
- Webhook notes: Asana supports webhooks for resource-level events. Webhooks can be registered on workspaces, projects, tasks, and other resources. User-related events (e.g., membership changes) can be captured via workspace-level webhooks.
- Alternative event strategy: Poll GET /workspaces/{workspace_gid}/users periodically if webhooks are not feasible.
- Webhook events: user.added_to_workspace, user.removed_from_workspace, user.added_to_team, user.removed_from_team, task.assigned, task.unassigned
SCIM API status
SCIM available: Yes
SCIM version: 2.0
Plan required: Enterprise
Endpoint: https://app.asana.com/api/1.0/scim/v2
Supported operations: GET /Users – list users, GET /Users/{id} – get user by SCIM ID, POST /Users – provision new user, PUT /Users/{id} – replace user attributes, PATCH /Users/{id} – update user attributes, DELETE /Users/{id} – deprovision user, GET /Groups – list groups (teams), POST /Groups – create group, PATCH /Groups/{id} – update group membership, DELETE /Groups/{id} – delete group
Limitations:
- Requires Enterprise or Enterprise+ plan.
- SAML SSO must be configured as a prerequisite.
- Supported IdPs include Okta, Microsoft Entra ID (Azure AD), Google Workspace, and OneLogin.
- SCIM provisioning manages workspace membership; team membership is managed via Groups.
- User deprovisioning via SCIM deactivates the user but does not delete their data.
- Custom attribute mapping is limited to standard SCIM schema attributes.
Common scenarios
Three scenarios cover the majority of programmatic user management needs:
List all workspace users with email: GET /workspaces/{workspace_gid}/users?opt_fields=name,email&limit=100 - paginate via next_page.offset until exhausted. Without opt_fields=email, email is not returned.
Deprovision a departing employee (REST only): POST /workspaces/{workspace_gid}/removeUser with the user's GID. This deactivates the user but does not delete data or reassign tasks - follow up with PATCH /tasks/{task_gid} to set a new assignee GID on open work items.
Provision via SCIM (Enterprise only): Configure your IdP against https://app.asana.com/api/1.0/scim/v2. The IdP sends POST /scim/v2/Users with userName, name.givenName, name.familyName, and active=true. Note: the user must accept an invitation email before they appear as active in REST API workspace user lists - SCIM provisioning and REST API visibility are not synchronous.
Provision a new employee into Asana via SCIM
- Ensure Enterprise plan is active and SAML SSO is configured.
- Configure your IdP (e.g., Okta) with Asana's SCIM endpoint: https://app.asana.com/api/1.0/scim/v2.
- Assign the user to the Asana application in your IdP.
- IdP sends POST /scim/v2/Users with userName (email), name.givenName, name.familyName, and active=true.
- Asana provisions the user into the organization; user receives an invitation email.
- Optionally assign the user to a SCIM Group (team) via PATCH /scim/v2/Groups/{team_gid}.
Watch out for: The user must accept the invitation before they appear as active in workspace user lists via the REST API.
List all users in a workspace with email addresses
- Obtain a PAT or OAuth token with 'default' scope.
- Call GET /api/1.0/workspaces to retrieve the workspace GID.
- Call GET /api/1.0/workspaces/{workspace_gid}/users?opt_fields=name,email&limit=100.
- Check response for next_page.offset; if present, repeat with ?offset={offset_token} until next_page is null.
- Collect all user objects across pages.
Watch out for: Without ?opt_fields=email, the email field is not returned in compact responses.
Deprovision a departing employee
- If using SCIM: deactivate or unassign the user in your IdP; IdP sends PATCH /scim/v2/Users/{id} with active=false or DELETE /scim/v2/Users/{id}.
- If using REST API only: call POST /api/1.0/workspaces/{workspace_gid}/removeUser with the user's GID.
- Verify the user no longer appears in GET /api/1.0/workspaces/{workspace_gid}/users.
- Reassign open tasks if needed using PATCH /api/1.0/tasks/{task_gid} with assignee set to another user's GID.
Watch out for: Removing a user from the workspace does not delete their data. Tasks remain and must be manually reassigned or handled via automation.
Why building this yourself is a trap
The REST API cannot create net-new users from scratch; POST /workspaces/{workspace_gid}/addUser sends an invitation email, and the user is not fully active until they accept. This makes fully automated, zero-touch provisioning impossible via REST alone - SCIM is required for that, and SCIM requires Enterprise plus a pre-configured SAML SSO setup.
A second trap: user email addresses cannot be updated via the REST API; use PATCH /scim/v2/Users/{id} for email changes. Finally, removing a user from a team (POST /teams/{team_gid}/removeUser) does not remove them from the workspace - workspace-level deprovisioning is a separate call and the only action that revokes access.
Automate Asana workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.