hamburger icon "
BETA
curl

Overview

Basics

Helpful code snippets will show up in this column. You can choose your language in the navigation above!

curl https://app.asana.com/api/1.0/users/me \
  -H "Authorization: Bearer 0/a7f89e98g007e0s07da763a"

The Asana API is a RESTful interface, providing programmatic access to much of the data in the system. It provides predictable URLs for accessing resources, and uses built-in HTTP features to receive commands and return responses. This makes it easy to communicate with from a wide variety of environments, from command-line utilities to gadgets to the browser URL bar itself.

The API accepts JSON or form-encoded content in requests and returns JSON content in all of its responses, including errors. Only the UTF-8 character encoding is supported for both requests and responses.

Notes on Pagination

Pagination is an important concept when working with queries for multiple objects. Requests with large result sets may timeout or be truncated; therefore, pagination is strongly encouraged to ensure both you and your users have the best experience when using the Asana API.

Happy coding!


Object Hierarchy

Asana object hierarchy

Asana is a work tracking and collaboration tool. This guide is designed to give developers building on Asana’s API a brief overview of how Asana is structured. It’s not meant to be exhaustive and may be too basic for experienced Asana users, but read on if you're not a user of Asana who uses it regularly. The intention is to describe the fundamental elements of Asana to help you scope apps and avoid common points of confusion.

How work in Asana is organized

Tasks

Tasks are the basic unit of action in Asana. Tasks have many fields including a single assignee, name, notes, followers (aka collaborators), likes, and comments. Tasks inherit custom fields from their parent project(s). Custom fields values are set for each individual task.

In addition to standard Create / Read / Update / Delete operations, there are a few things to watch out for when working with tasks:

  1. Tasks can be orphaned and belong to no projects, they can belong to one project, or they can be multi-homed across two or more projects. The memberships field is a collection of the projects with which the task is associated.
  2. Tasks can be multi-homed as subtasks. For example, task A can be in project B and the same task A can also be a subtask of task C.

Projects

A project is a collection of tasks that can be viewed as a list, board, timeline, and calendar. Projects can only exist in a single organization or workplace and only belong to a single team. Projects can be public in the team or private to project members. Among the many fields associated with projects, they can have global (shared across the organization) or local (project-specific) custom fields. A project’s custom fields will be displayed on each task within the project.

Portfolios

Portfolios are collections of projects (or other portfolios). Custom fields can be added to portfolios in addition to standard fields that are displayed on every portfolio. These fields provide a high-level overview of the status of each project within the portfolio.

Sections

A section is a group of tasks within a project. Sections let you divide tasks into categories, workflow stages, priorities, and more.

Subtasks

A subtask is exactly the same as tasks in a project except that one (and only one) of its parents is a task (although subtasks can also simultaneously be organized into projects). To check if a task has a subtask, include the num_subtasks field when fetching the parent task.

Things to note when working with subtasks:

  1. Subtasks do not inherit the projects of their parent tasks.
  2. There can be up to 5 levels of subtasks below a task. We do not recommend making sub-subtasks.
  3. There is no way to fetch all subtasks of all tasks in a project in a single request.

How users of Asana are organized

Workspaces

A workspace is the highest-level organizational unit in Asana. All projects, tasks, and teams have an associated workspace.

Organizations

An organization is a special kind of workspace that represents a company. Organizations connect all the employees at a company using Asana in a single space based on the company’s shared email domain. In an organization, you can group your projects into teams.

Teams

Teams are a subset of users in an organization who collaborate on projects together. Every project in an organization is associated with one team. Team conversations are not currently available in the API.

Users

A user object represents an account in Asana that can be given access to various workspaces, projects, and tasks. Asana accounts are free and tied to individuals; Asana accounts grant access to one or more shared Workspaces and Organizations to collaborate with other Asana users.

Guest Users

Users can invite clients, contractors, customers, or anyone else who does not have an email address at an approved Organization email domain. These users join as Organization Guests. Guests have limited access in an Organization -- they can only see what’s explicitly shared with them.

Note: it can be advantageous to use guests to create bot accounts. Due to the access restrictions, bots created from a guest account Personal Access Token can be given fine-grained access to only the data that it needs to use.


Authentication Basics

Asana supports a few methods of authenticating with the API.

"Authorization: Bearer ACCESS_TOKEN"
  • OAuth 2.0 We require that applications designed to access the Asana API on behalf of multiple users implement OAuth 2.0.
  • Personal Access Token Personal Access Tokens are designed for accessing the API from the command line or from personal applications.

OAuth

Most of the time, OAuth is the preferred method of authentication for developers, users, and Asana as a platform. If you're new to OAuth, take a moment to learn about it here. It's not as scary as you might think!

In addition to learning about how to use OAuth on the Asana platform here, feel free to take a look at the official OAuth spec!

At its core, OAuth is a mechanism for applications to access the Asana API on behalf of a user without the application having access to the username and password. Instead, apps get a token which they can use with their own application credentials to make API calls.

What is OAuth?

If you're using a library to handle this or already understand OAuth and have registered an OAuth application, you may want to skip ahead to the quick reference.

  1. If you have not already, you will need to register an application. Take note of the client ID, an application's username, and the client secret, an application's password (protect it like one)!

  2. A user will arrive at your application and click a button that says "Connect with Asana."

  3. This takes the customer to the User Authorization Endpoint, which displays a page asking the user if they would like to grant access to your third-party application.

  4. If the customer clicks "Allow", they are redirected back to the application, bringing along a special code as a query parameter. (We are assuming the Authorization Code Grant flow, which is the most common.)

  5. The application can now use the Token Exchange Endpoint to exchange the code, together with the Client Secret, for a Bearer Token (which lasts an hour) and a Refresh Token (which can be used to fetch new Bearer Tokens when the current one expires).

  6. The application can make requests of the API using this Bearer Token for the next hour.

  7. Once the Bearer Token expires, the application can again use the Token Exchange Endpoint to exchange the Refresh Token for a new Bearer Token. (This can be repeated for as long as the user has authorized the application.)

We definitely recommend using a library to take care of the nitty-gritty of OAuth, but hopefully this helps demystify the process somewhat.

Register an Application

You must first register your application with Asana to receive a client ID and client secret. Fortunately, this process is fast and easy: visit your Account Settings dialog, click the Apps tab, and "Add New Application".

You must supply your new application with:

  • App Name - A name for your application. A user will see this name when your application requests permission to access their account as well as when they review the list of apps they have authorized.
  • App URL - The URL where users can access your application or, in the case of native applications, this can be a link to setup or support instructions. Note that this URL must start with "http" or "https".
  • Redirect URL - As described in the OAuth specification, this is where the user will be redirected upon successful or failed authentications. Native or command line applications should use the special redirect URL urn:ietf:wg:oauth:2.0:oob. For security reasons, non-native applications must supply a "https" URL (more on this below).
  • Icon - Optionally, you can upload an icon to enhance the recognizability of the application when users are authenticating.

Note that all of these attributes can be changed later, so don't worry too much right away.

Once you have created an app, the details view will include a Client ID, needed to uniquely identify your app to the Asana API, as well as a Client Secret.

Note Your Client Secret is a secret, it should never be shared with anyone or checked into source code that others could gain access to.

Quick Reference

  • Applications can be created from the "Apps" tab of your account settings, where you will find your Client ID and Client Secret.
  • The endpoint for user authorization is https://app.asana.com/-/oauth_authorize
  • The endpoint for token exchange is https://app.asana.com/-/oauth_token
  • Asana supports the Authorization Code Grant flow.
  • Once an access token has been obtained your application can make calls on behalf of the user

User Authorization Endpoint

Request

Send a user to oauth_authorize

<a href="https://app.asana.com/-/oauth_authorize
?client_id=753482910
&redirect_uri=https://my.app.com
&response_type=code
&state=thisIsARandomString
&code_challenge_method=S256
&code_challenge=671608a33392cee13585063953a86d396dffd15222d83ef958f43a2804ac7fb2
&scope=default">Authenticate with Asana</a>

Your app redirects the user to https://app.asana.com/-/oauth_authorize, passing parameters along as a standard query string:

ParameterDescription
client_idrequired The Client ID uniquely identifies the application making the request.
redirect_urirequired The URI to redirect to on success or error. This must match the Redirect URL specified in the application settings.
response_typerequired Must be either code or id_token, or the space-delimited combination code id_token.
stateoptional Encodes state of the app, which will be returned verbatim in the response and can be used to match the response up to a given request.
code_challenge_methodPKCE The hash method used to generate the challenge. Typically S256.
code_challengePKCE The code challenge used for PKCE.
scopeoptional A space-delimited set of one or more scopes to get the user's permission to access. Defaults to the default OAuth scope if no scopes are specified.

Response

If either the client_id or redirect_uri do not match, the user will simply see a plain-text error. Otherwise, all errors will be sent back to the redirect_uri specified.

The user then sees a screen giving them the opportunity to accept or reject the request for authorization. In either case, the user will be redirected back to the redirect_uri.

User is redirected to the redirect_uri

https://my.app.com?code=325797325&state=thisIsARandomString

When using the response_type=code, your app will receive the following parameters in the query string on successful authorization.

ParameterDescription
codeIf response_type=code in the request, this is the code your app can exchange for a token
stateThe state parameter that was sent with the authorizing request

You should check that the state is the same in this response as it was in the request. You can read more about the state parameter here.

OAuth Scopes

The Asana API supports a small set of OAuth scopes you can request using the scope parameter during the user authorization step of your authentication flow. Multiple scopes can be requested at once as a space-delimited list of scopes. An exhaustive list of the supported scopes is provided here:

ScopeAccess provided
defaultProvides access to all endpoints documented in our API reference. If no scopes are requested, this scope is assumed by default.
openidProvides access to OpenID Connect ID tokens and the OpenID Connect user info endpoint.
emailProvides access to the user's email through the OpenID Connect user info endpoint.
profileProvides access to the user's name and profile photo through the OpenID Connect user info endpoint.

Token Exchange Endpoint

Request

When your app receives a code from the authorization endpoint, it can now be exchanged for a proper token.

If you have a client_secret, this request should be sent from your secure server. The browser should never see your client_secret.

App sends request to oauth_token

{
  "grant_type": "authorization_code",
  "client_id": "753482910",
  "client_secret": "6572195638271537892521",
  "redirect_uri": "https://my.app.com",
  "code": "325797325",
  "code_verifier": "fdsuiafhjbkewbfnmdxzvbuicxlhkvnemwavx"
}

Your app should make a POST request to https://app.asana.com/-/oauth_token, passing the parameters as part of a standard form-encoded post body.

ParameterDescription
grant_typerequired One of authorization_code or refresh_token. See below for more details.
client_idrequired The Client ID uniquely identifies the application making the request.
client_secretrequired The Client Secret belonging to the app, found in the details pane of the developer console.
redirect_urirequired Must match the redirect_uri specified in the original request.
coderequired This is the code you are exchanging for an authorization token.
refresh_tokensometimes required If grant_type=refresh_token this is the refresh token you are using to be granted a new access token.
code_verifierPKCE This is the string previously used to generate the code_challenge.

The token exchange endpoint is used to exchange a code or refresh token for an access token.

Response

In the response, you will receive a JSON payload with the following parameters:

{
  "access_token": "f6ds7fdsa69ags7ag9sd5a",
  "expires_in": 3600,
  "token_type": "bearer",
  "refresh_token": "hjkl325hjkl4325hj4kl32fjds",
  "data": {
    "id": "4673218951",
    "name": "Greg Sanchez",
    "email": "gsanchez@example.com"
  }
}
ParameterDescription
access_tokenThe token to use in future requests against the API
expires_inThe number of seconds the token is valid, typically 3600 (one hour)
token_typeThe type of token, in our case, bearer
refresh_tokenIf exchanging a code, a long-lived token that can be used to get new access tokens when old ones expire.
dataA JSON object encoding a few key fields about the logged-in user, currently id, name, and email.

Authorization Code Grant

To implement the Authorization Code Grant flow (the most typical flow for most applications), there are three steps:

  1. Send the user to the authorization endpoint so that they can approve access of your app to their Asana account

  2. Receive a redirect back from the authorization endpoint with a code embedded in the parameters

  3. Exchange the code via the token exchange endpoint for a **refresh_token** and, for convenience, an initial access_token.

  4. When the short-lived access_token expires, the **refresh_token** can be used with the token exchange endpoint, without user intervention, to get a fresh access_token.

The token that you have at the end can be used to make calls to the Asana API on the user's behalf.

Proof Key for Code Exchange (PKCE) OAuth Extension

User Authorization Endpoint

{
  ...
  "code_challenge": "671608a33392cee13585063953a86d396dffd15222d83ef958f43a2804ac7fb2",
  "code_challenge_method": "S256"
}

Token Exchange Endpoint

{
  ...
  "code_verifier": "fdsuiafhjbkewbfnmdxzvbuicxlhkvnemwavx"
}

PKCE proves the app that started the authorization flow is the same app that finishes the flow. You can read more about it here.

Here's what you need to know:

  1. Whenever a user wants to oauth with Asana, your app server should generate a random string called the code_verifier. This string should be saved to the user (as every code_verifier should be unique per user). This should stay on the app server and only be sent to the Token Exchange Endpoint.

  2. Your app server will hash the code_verifier with SHA256 to get a string called the code_challenge. Your server will give the browser only the code_challenge & code_challenge_method. The code_challenge_method should be the string "S256" to tell Asana we hashed with SHA256. More specifically, code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier))).

  3. The browser includes code_challenge & code_challenge_method when redirecting to the User Authorization Endpoint.

  4. The app server should include the code_verifier in it's request to the Token Exchange Endpoint.

Asana confirms that hashing the code_verifier with the code_challenge_method results in the code_challenge. This proves to Asana the app that hit the User Authorization Endpoint is the same app that hit the Token Exchange Endpoint.

Secure Redirect Endpoint

As the redirect from the authorization endpoint in either grant procedure contains a code that is secret between Asana's authorization servers and your application, this response should not occur in plaintext over an unencrypted http connection. Asana is deprecating this behavior. We're enforcing the use of https redirect endpoints for new application registrations, and will soon stop calling back to http apps during the authorization process.

For non-production or personal use, you may wish to check out stunnel, which can act as a proxy to receive an encrypted connection, decrypt it, and forward it on to your application. For development work, you may wish to create a self-signed SSL/TLS certificate for use with your web server; for production work we recommend purchasing a certificate from a certificate authority. A short summary of the steps for each of these processes can be read here.

Your application will need to be configured to accept SSL/TLS connections for your redirect endpoint when users become authenticated, but for many apps, this will simply require a configuration update of your application server. Instructions for Apache and Nginx can be found on their respective websites, and most popular application servers will contain documentation on how to proceed.

Personal Access Token

Personal Access Tokens are a useful mechanism for accessing the API in scenarios where OAuth would be considered overkill, such as access from the command line and personal scripts or applications. A user can create many, but not unlimited, personal access tokens. When creating a token you must give it a description to help you remember what you created the token for.

Personal Access Tokens should be used similarly to OAuth access tokens when accessing the API, passing them in the Authorization header.

curl https://app.asana.com/api/1.0/users/me \
  -H "Authorization: Bearer ACCESS_TOKEN"

You should regularly review the list of personal access tokens you have created and deauthorize those that you no longer need.

Note: Remember to keep your tokens secret; treat them just like passwords! They act on your behalf when interacting with the API. Don't hardcode them into your programs; instead, opt to use them as environment variables.

OpenID Connect

Asana also supports the OpenID Connect protocol for authenticating Asana users with your applications. This means that, in addition to the normal code and token response types for the OAuth flow, you can also use the id_token response type.

For this response type, you are not granted an access token for the API, but rather given a signed Json Web Token containing the user's ID along with some metadata. If you want to allow users to log into your services using their Asana account, the OpenID Connect protocol is an ideal way to authenticate an Asana user. To obtain an ID token, you must request the openid scope during the authentication flow.

It is also possible to obtain an ID token alongside an authorization code in the authorization code grant flow by using the (space-delimited) code id_token response type. If you do, the redirect parameters will include the ID token in addition to everything you would normally receive.

To access additional information about the user in a standardized format, we also expose a user info endpoint that can provide the user's name, email address, and profile photo. This data is available by making a GET request to https://app.asana.com/api/1.0/openid_connect/userinfo with an OAuth access token that has the openid scope. Depending on the scopes tied to that token, you will receive different pieces of data. Refer to our list of OAuth scopes to determine which additional scopes you need to get the data you want.

Metadata about our OpenID Connect implementation is also made available through OpenID Connect's discovery protocol. Making an unauthenticated GET request to https://app.asana.com/api/1.0/.well-known/openid-configuration will provide all the details of our implementation necessary for you to use OpenID Connect with Asana's API.


Errors

Missing authorization header

GET https://app.asana.com/api/1.0/users/me HTTP/1.1
HTTP/1.1 401 Not Authorized
{
  "errors": [
  {
    "message": "Not Authorized"
  }
  ]
}

Sadly, sometimes requests to the API are not successful. Failures can occur for a wide range of reasons. In all cases, the API should return an HTTP Status Code that indicates the nature of the failure (below), with a response body in JSON format containing additional information.

In the event of a server error the response body will contain an error phrase. These phrases are automatically generated using the node-asana-phrase library and can be used by Asana support to quickly look up the incident that caused the server error.

Bad request parameters

GET https://app.asana.com/api/1.0/tasks HTTP/1.1
Authorization: Bearer <personal_access_token>
HTTP/1.1 400 Bad Request
{
  "errors": [
  {
    "message": "workspace: Missing input"
  }
  ]
}

Asana had a problem

GET https://app.asana.com/api/1.0/users/me HTTP/1.1
Authorization: Bearer <personal_access_token>
HTTP/1.1 500 Server Error
{
  "errors": [
  {
    "message": "Server Error",
    "phrase": "6 sad squid snuggle softly"
  }
  ]
}



CodeMeaningDescription
200SuccessIf data was requested, it will be available in the data field at the top level of the response body.
201Created
(for object creation)
Its information is available in the data field at the top level of the response body. The API URL where the object can be retrieved is also returned in the Location header of the response.
400Bad RequestThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401UnauthorizedA valid authentication token was not provided with the request, so the API could not associate a user with the request.
402Payment RequiredThe request was valid, but the queried object or object mutation specified in the request is only available to premium organizations and workspaces.
403ForbiddenThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404Not FoundEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
429Too Many RequestsYou have exceeded one of the enforced rate limits in the API. See the documentation on rate limiting for more information.
500Internal Server ErrorThere was a problem on Asana's end.



In the event of an error, the response body will contain an errors field at the top level. This contains an array of at least one error object, described below:

ExampleDescription
messageproject: Missing input Message providing more detail about the error that occurred, if available.
phrase6 sad squid snuggle softly 500 errors only. A unique error phrase which can be used when contacting developer support to help identify the exact occurrence of the problem in Asana's logs.


Rate Limits

To protect the stability of the API and keep it available to all users, Asana enforces multiple kinds of rate limiting. Requests that hit any of our rate limits will receive a 429 Too Many Requests response, which contains the standard Retry-After header indicating how many seconds the client should wait before retrying the request.

Limits are allocated per authorization token. Different tokens will have independent limits.

The client libraries respect the rate-limited responses and will wait the appropriate amount of time before automatically retrying the request, up to a configurable maximum number of retries.

Standard Rate Limits

Request

GET https://app.asana.com/api/1.0/users/me HTTP/1.1
Authorization: Bearer <personal_access_token>

Response

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 30

{
  "errors": [
  {
    "message": "You've made too many requests and hit a rate limit. Please retry after the given amount of time."
  }
  ]
}

Our standard rate limiter imposes a quota on how many requests can be made in a given window of time. Our limits are based on minute-long windows, and differ depending on whether the domain is premium or not. We may change these quotas or add new quotas (such as maximum requests per hour) in the future.

Domain typeMaximum requests per minute
Free150
Premium1500

In addition, calls to the search API are limited to 60 requests per minute. The duplication endpoints are limited to 5 concurrent jobs.

Although the quota is defined per minute, it is evaluated more frequently than once per minute, so you may not need to wait for a full minute before retrying your request. For requests rejected by this limiter, the Retry-After header has the result of this calculation.

Requests rejected by this limiter still count against the quotas so that ignoring the Retry-After header will result in fewer and fewer requests being accepted during the subsequent tine windows.

Concurrent Request Limits

In addition to limiting the total number of requests in a given time window, we also limit the number of requests being handled at any given instant. We may change these limits or add new limits in the future.

Request typeMaximum concurrent requests
Reads GET50
Writes POST, PUT, PATCH, DELETE15

For example, if you have 50 read requests in-flight and attempt to make another read request, the API will return a 429 Too Many Requests error. The read and write limits are independent of each other, so the number of read requests you make at one time will have no impact on the number of write requests you can make.

Responses for requests rejected by this concurrent request limiter will contain a Retry-After header specifying a duration long enough such that the other in-flight requests are guaranteed to have either completed or timed out.

Cost Limits

Objects in Asana are connected to each other in a graph. Some examples of links in this graph are:

  • a task object is linked to a user object as the assignee
  • a user is linked to the projects it's following
  • a tag is linked to all its tasks
  • a task is linked to all its subtasks
  • a task is linked to all its custom field values

Depending on the kind of requests you make to our API, our servers have to traverse different parts of the graph. The sizes of these parts directly influence how expensive it is for our servers to build the API responses. For example, fetching just the name and gid of a task requires very few resources and no traversal of the graph, but fetching all tasks in a project and all their attributes (assignee, followers, custom fields, likes) can require following several thousand links in the graph.

Because there can be a wide range in the cost of any given API request in terms of the computational resources and database load, the standard rate limits are not always enough to maintain stability of the API. In the past, when we’ve received bursts of expensive requests, our typical course of action has been to block the offending authorization token and reject all future requests, resulting in confusion for both the user and the app developer. Instead, to protect against the extreme cases where API requests require inordinate traversal of the graph, we impose an additional limit based on the computational cost.

The cost we associate with a request isn't equivalent to the number of links in the subset of the graph involved, but it is roughly proportional. The cost of a request is calculated after the response has been fully built and we know how much data we needed to fetch from our databases to build it. This cost is then deducted from a quota, and the response is returned. Because the cost of a request is not known until we’ve built the response, we allow this deduction to result in a net negative quota. The request that causes the quota to become negative will receive the expected response and not be rejected.

When a request is received, if the remaining quota is not positive, the new request is rejected with a 429 Too Many Requests. As with the standard rate limits, this quota is defined per-minute but is updated on a more frequent interval. The Retry-After header will specify how long you must wait for the quota to become positive again.

The vast majority of developers will be unaffected by the cost limit, and the quota is set sufficiently high that it only affects users making requests that would compromise the stability of the API. Rather than unconditionally blocking their token from the API, this cost limiter will permit them to continue operation at a slower but safe and stable rate.


Rich Text

Example Rich Text

<body>All these new tasks are <em>really</em> getting disorganized, so <a data-asana-gid="4168112"/> just made the new <a data-asana-gid="5732985"/> project to help keep them organized. <strong>Everyone</strong> should start using the <a data-asana-gid="6489418" data-asana-project="5732985"/> when adding new tasks there.</body>

Note: In preparation for our upcoming migration to string IDs (referred to as gids in the API) this new feature uses only gids and not ids. Read more about it in our community post.

The web product offers a number of rich formatting features when writing task notes, comments, project descriptions, and project status updates. These features include bold, italic, underlined, and monospaced text, as well as bulleted and numbered lists. Additionally, users can "@-mention" other users, tasks, projects, and many other objects within Asana to create links.

The rich text field name for an object is equivalent to it's plain text field name prefixed with html_. The following object types in Asana support rich text:

ObjectPlain text fieldRich text field
Tasksnoteshtml_notes
Projectsnoteshtml_notes
Storiestexthtml_text
Project status updatestexthtml_text
Teamsdescriptionhtml_description

Reading rich text

Python

from lxml import etree

html_text = "<body>...</body>"
root = etree.HTML(html_text)
user_ids = root.xpath('//a[@data-asana-type="user"]/@data-asana-gid')
for user_id in user_ids:
    print(user_id)

Java

import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import java.util.List;

XML root = new XMLDocument("<body>...</body>");
List<String> userIds = root.xpath("//a[@data-asana-type=\"user\"]/@data-asana-gid");
for (String userId : userIds) {
    System.out.println(userId);
}

Javascript

var htmlText = '<body>...</body>'
var parser = new DOMParser()
var doc = parser.parseFromString(htmlText, "text/html")
var iterator = doc.evaluate(
    '//a[@data-asana-type="user"]/@data-asana-gid', doc, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE)
var node = iterator.iterateNext()
while (node) {
    console.log(node.nodeValue);
    node = iterator.iterateNext();
}

Rich text in the API is formatted as an HTML fragment, which is wrapped in a root <body> tag. Rich text is guaranteed to be valid XML; there will always be a root element, all tags will be closed, balanced, and case-sensitive, and all attribute values will be quoted. The following is a list of all the tags that are currently returned by the API:

TagMeaning in Asana
<body>None
<strong>Bold text
<em>Italic text
<u>Underlined text
<s>Strikethrough text
<code>Monospaced text
<ol>Ordered list
<ul>Unordered list
<li>List item
<a>Link

Note: This list can expand as new features are introduced to the Asana web product. Treat rich text as you would treat arbitrary HTML, and ensure that your code doesn't break when it encounters a tag not on this list.

While links are easy to understand when users view the rendered in the Asana web product, an <a> tag and its href alone are insufficient to programmatically understand what the target of the link is. This is confused further by the fact that the formats of these links are frequently ambiguous. For example, an @-mention to a user generates a link to their "My Tasks", which looks identical to a link to a normal project.

Because of this, the API will return additional attributes in <a> tags to convey meaningful information about the target. The following is a complete list of attributes we may return inside an <a> tag, in addition to the usual href:

AttributeMeaning
data-asana-accessibleBoolean, representing whether or not the linked object is accessible to the current user. If the resource is inaccessible, no other data-asana-* attributes will be included in the tag.
data-asana-typeThe type of the referenced object. One of user, task, project, tag, conversation, status_update, team, or search.
data-asana-gidThe GID of the referenced object. If the referenced object is a user, this is the user's GID.
data-asana-projectIf the type of the referenced object is a task, and the link references that task in a particular project, this is the GID of that project.
data-asana-tagIf the type of the referenced object is a task, and the link references that task in a particular tag, this is the GID of that tag.

Here are some examples of how this behavior manifests:

  • Suppose a user with a name of "Tim" and a user GID of "53421" is @-mentioned. This will create a link to their "My Tasks" which is a project with a GID of "56789"
    • The raw link generated in Asana will be https://app.asana.com/0/56789/list.
    • The <a> tag returned in the API will be <a href="https://app.asana.com/0/56789/list" data-asana-accessible="true" data-asana-type="user" data-asana-gid="54321">Tim</a>.
  • Suppose a link to a task with name "Buy milk" and GID "1234" being viewed in a project with GID "5678" is copied from the address bar and pasted into a comment.
    • The raw link generated in Asana will be https://app.asana.com/0/5678/1234.
    • The <a> tag returned in the API will be <a href="https://app.asana.com/0/5678/1234" data-asana-accessible="true" data-asana-type="task" data-asana-gid="1234" data-asana-project="5678">Buy milk</a>
  • Suppose another user @-mentions a project with GID "5678" that is private and not visible to you in the web product.
    • The raw link generated in Asana will be https://app.asana.com/0/5678/list.
    • The <a> tag returned in the API will be <a href="https://app.asana.com/0/5678/list" data-asana-accessible="false">Private Link</a>

Here's an example of what a complete rich comment might look like in the API:

<body>All these new tasks are <em>really</em> getting disorganized, so <a href="https://app.asana.com/0/4168466/list" data-asana-accessible="true" data-asana-type="user" data-asana-gid="4168112">Tim Bizzaro</a> just made the new <a href="https://app.asana.com/0/5732985/list" data-asana-accessible="true" data-asana-type="project" data-asana-gid="5732985">Work Requests</a> project to help keep them organized. <strong>Everyone</strong> should start using the <a href="https://app.asana.com/0/5732985/6489418" data-asana-accessible="true" data-asana-type="task" data-asana-gid="6489418" data-asana-project="5732985">Request template</a> when adding new tasks there.</body>

Writing rich text

When writing rich text to the API, you must provide similarly structured, valid XML. The text must be wrapped in a <body> tag, all tags must be closed, balanced, and match the case of supported tags, and attributes must be quoted. Invalid XML, as well as unsupported tags, will be rejected with a 400 Bad Request error. Only <a> tags support attributes, and any attributes on other tags will be similarly rejected.

For <a> tags specifically, to make it easier to create @-mentions through the API, we only require that you provide the GID of the object you wish to reference. If you have access to that object, the API will automatically generate the appropriate href and other attributes for you. For example, to create a link to a task with GID "123", you can send the tag <a data-asana-gid="123"/> which will then be expanded to <a href="https://app.asana.com/0/0/123/f" data-asana-accessible="true" data-asana-type="task" data-asana-gid="123">Task Name</a>. You can also generate a link to a task in a specific project or tag by including a data-asana-project or data-asana-tag attribute in the <a> tag. All other attributes, as well as the contents of the tag, are ignored.

If you do not have access to the referenced object when you try to create a link, the API will not generate an href for you, but will instead look for an href you provide. This allows you to write back <a> tags unmodified even if you do not have access to the resource. If you do not have access to the referenced object and no href is provided, your request will be rejected with a 400 Bad Request error. Similarly, if you provide neither a GID nor a valid href, the request will be rejected with the same error.


Pagination

Paginating requests for object sets that may be large is highly recommended. For requests that will return large result sets the API may truncate the result or timeout attempting to gather the data. Pagination ensures a more reliable experience by limiting requests to a smaller number of objects at a time, ultimately getting you the results faster; should there be more results, the API will return an offset that will allow you to access the next page.

Strongly prefer paginated requests {#prefer-pagination}

For all new features in the Asana API, we're making pagination required by specifying a value for the limit parameter. Though they may return more results, our current unpaginated requests are still ultimately subject to a timeout limit, which means requests that work successfully one day may fail later due to factors such as server load and network latency.

Pagination limits provide a mechanism to specify a page size that we should always be able to serve regardless of these factors. To prevent current integrations from breaking, pagination is not enabled by default on "grandfathered" endpoints; instead, you can request paginated results by providing the optional limit parameter in your query. We will be working to deprecate requests to these endpoints in the future.

Request

curl "https://app.asana.com/api/1.0/tasks?project=1337&limit=5&offset=eyJ0eXAiOJiKV1iQLCJhbGciOiJIUzI1NiJ9" \
  -H "Authorization: Bearer <personal_access_token>"

Response

{
  "data": [
    {
      "id": 1000,
      "name": "Task 1",
      ...
    },
    ...
  ],
  "next_page": {
    "offset": "yJ0eXAiOiJKV1QiLCJhbGciOiJIRzI1NiJ9",
    "path": "/tasks?project=1337&limit=5&offset=yJ0eXAiOiJKV1QiLCJhbGciOiJIRzI1NiJ9",
    "uri": "https://app.asana.com/api/1.0/tasks?project=1337&limit=5&offset=yJ0eXAiOiJKV1QiLCJhbGciOiJIRzI1NiJ9"
  }
}

Note that all of Asana's official client libraries support pagination by default.

When making a paginated request, the API will return a number of results as specified by the limit parameter. If more results exist, then the response will contain a next_page attribute, which will include an offset, a relative path attribute, and a full uri attribute. If there are no more pages available, next_page will be null and no offset will be provided. Do note that an offset token will expire after some time, as data may have changed.

When making paginated requests you are able to page through all objects for a particular query up to 100 objects at a time. Alternatively your query will be truncated at about 1000 objects. In addition, when issuing non-paginated requests to organizations with a large number of objects queries may time out before returning. For these reasons, we recommend that you paginate all requests to the API.

ParameterDescription
Limit20 The number of objects to return per page. The value must be between 1 and 100.
OffseteyJ0eXAiOJiKV1iQLCJhbGciOiJIUzI1NiJ9 An offset to the next page returned by the API. A pagination request will return an offset token, which can be used as an input parameter to the next request. If an offset is not passed in, the API will return the first page of results.

Note: You can only pass in an offset that was returned to you via a previously paginated request.

This method returns paginated results for tasks from a project.


Input/Output Options

GET url params

?opt_pretty
?opt_fields=followers,assignee

PUT or POST body options

options: { 
  pretty: true,
  fields: ["followers", "assignee"]
}

In addition to providing fields and their values in a request, you may also specify options to control how your request is interpreted and how the response is generated. For GET requests, options are specified as URL parameters prefixed with opt_. For POST or PUT requests, options are specified in the body. If the body uses the application/x-www-form-urlencoded content type, then options are prefixed with opt_ just like for GET requests. If the body uses the application/json content type, then options are specified inside the top-level options object (a sibling of the data object).

?opt_fields=name,notes&opt_pretty response

HTTP/1.1 200
{
  "data": {
    "name": "Make a list",
    "notes": "Check it twice!",
    "gid": 1224
  }
}

These options can be used in combination in a single request, though some of them may conflict in their impact on the response.

OptionDescription
prettyProvides the response in "pretty" output. In the case of JSON this means doing proper line breaking and indentation to make it readable. This will take extra time and increase the response size so it is advisable only to use this during debugging.
fieldsSome requests return compact representations of objects, to conserve resources and complete the request more efficiently. Other times requests return more information than you may need. This option allows you to list the exact set of fields that the API should be sure to return for the objects. The field names should be provided as paths, described below.

The gid of included objects will always be returned, regardless of the field options.

SELECTING FIELDS

opt_fields nesting

"data": {                       < this
  "gid": 1001,
  "name": "Feed the cat",       < this.name
  "workspace": {                < this.workspace
    "gid": 14916,
    "name": "Shared Projects",  < this.workspace.name
  },
  "followers": [{               < this.followers
    "gid": 1234,
    "email": "tbizarro@…",      < this.followers.email
  }, {
    "gid": 5678,
    "email": "gsanchez@…",      < this.followers.email
  }]
}

Some output options allow you to reference fields of objects to include in the response. The way to specify a field is by path. A path is made up of a sequence of terms separated by the dot (.) operator. It takes the form this.a.b… where this refers to an object returned at the top level of the response, a the name of a field on a root object, b a field on a child object referred to by a, and so on.

For example, when retrieving a task or tasks, the path this.followers.email refers to the email field of all users mentioned in the followers field of the task or tasks returned. See the annotated output below:

There are also some advanced operators you can use for shorter syntax in selecting fields:

( .. | .. )

The group operator can be used in place of any whole term in a path, and will match any of a group of terms.

this.(followers|assignee).email will match the email field of the assignee object or any of the followers.


Custom External Data

Custom external data allows a client application to add app-specific metadata to Tasks in the API. The custom data includes a string gid that can be used to retrieve objects and a data blob that can store character strings.

The blob may store unicode-safe serialized data such as JSON or YAML. The external gid is capped at 1,024 characters, while data blobs are capped at 32,768 characters. Each object supporting external data can have one gid and one data blob stored with it. You can also use either or both of those fields.

The external gid field is a good choice to create a reference between a resource in Asana and another database, such as cross-referencing an Asana task with a customer record in a CRM, or a bug in a dedicated bug tracker. Since it is just a unicode string, this field can store numeric IDs as well as URIs, however, when using URIs extra care must be taken when forming queries that the parameter is escaped correctly. By assigning an external gid you can use the notation external:custom_id to reference your object anywhere that you may use the original object gid.

Note: that you will need to authenticate with Oauth, as the gid and data are app-specific, and these fields are not visible in the UI. This also means that external data set by one Oauth app will be invisible to all other Oauth apps. However, the data is visible to all users of the same app that can view the resource to which the data is attached, so this should not be used for private user data.

ParameterDescription
gid"contractor_name" The external gid. Max size is 1024 characters. Can be a URI.
data"{ \"time_estimate\": 3600, \"time_spent\": 1482 }" The external data blob. Max size is 32,786 characters.


Deprecations

Communicating about breaking changes

Whenever possible, the Asana API aims to preserve backwards compatibility for its users. Apps you write on top of the API now should, in ideal situations, continue to work indefinitely. However, there are a few rare cases where breaking changes are required. For example:

  • When we identify a stability threat, we may need to partially limit or entirely deprecate the culprit feature in the API.
  • When making a change in a backwards compatible way results in a cluttered, brittle, and confusing interface to Asana.
  • When not making a breaking change poses a security vulnerability, such as when we migrated from SSLv3 to TLS 1.0.

If a breaking change is required, the API team will provide a number of resources to help our developers get through the change:

  • We will communicate early, and through a variety of channels. We recommend that you subscribe to our developer newsletter and join the developers community forum.
  • We will provide a clear description of the change, how it affects your requests, and a migration plan to follow to transition through the deprecation.
  • We will designate a deprecation period during which you will be able to choose between both old and new behavior from the API, allowing you to test out the change without having to put your entire app at risk.

Response header notifications

While the previously mentioned communication channels are the best place to learn about upcoming changes, the API itself will also alert you of upcoming changes. Shortly after we post communication, the API will begin sending Asana-Change headers in the responses. These headers will have two or three pieces of information:

  • The unique name of the change
  • A URL to our blog post describing the change
  • Optionally, a flag indicating that your specific request would be affected.

This header may be present multiple times in the response if there are multiple ongoing changes. Here's an example of one of these headers:

Asana-Change: name=security_headers;info=https://asa.na/api-sh;affected=true
Asana-Change: name=other_change;info=https://asa.na/api-oc

Note: If your request is not affected, we will not claim affected=false. This is in case, during the deprecation, we detect that the change has a larger scope than initially thought. A request going from "you may be affected" to "you definitely are affected" is an acceptable update, but a request going from "you definitely are not affected" to "you definitely are affected" is not an acceptable update.

Request header options

During the deprecation period, you can test out how the API will behave by sending additional headers in your requests. Sending an Asana-Enable header including comma-separated names of features will turn those features on for that request. Sending an Asana-Disable header will do the opposite and turn those features off for that request.

If no header is specified, the default behavior will be determined by how far into the deprecation period we are:

  • Before the start of the deprecation period (the "start date") the feature will be disabled, and it will not be possible to enable it.
  • After the start date, the feature will be disabled by default, but you can begin choosing whether to enable or disable it.
  • In roughly the middle of the deprecation period (the "activation date") the default behavior will switch and the feature will be enabled by default. You can continue to disable it with the appropriate header.
  • At the end of the deprecation period (the "end date") the feature will be enabled and you will no longer be able to manually disable it.

The start, activation, and end dates will clearly be documented in our communications. These days may be pushed into the future if we discover that developers need more time to migrate their apps, but they are guaranteed to never occur sooner than documented.

These dates are arranged such that, if a developer happens to miss our communication and their app breaks when the default behavior changes on the activation date, they can begin sending the Asana-Disable header to restore the old behavior and use the remaining half of the deprecation period to make their app compatible.

Here's an example of how these headers would look:

Asana-Enable: security_headers,another_change
Asana-Disable: a_third_change

Aside from reserving the right to push the date of changes to a future date, the precise time during the activation date isn't specified. However, since the default will only affect your integration if you do not pass either the Asana-Enable or Asana-Disable headers for a particular deprecation you can ensure predictable behavior of our API for your app during the entire period, including throughout the activation date.

The way we recommend you implement these changes in your integration is this:

  • Whenever you detect a new Asana-Change header, log these requests and be sure to take note that a change is coming. Recall that the info key in the header will contain a link with the details.
  • Identify the nature of the upcoming deprecation and decide if your integration is sensitive to the change, paying particular attention to the cases where we return affected=true.
  • If changes are necessary in your integration, test the new behavior manually by making requests with Asana-Enable set to the name of the deprecation. This should provide a clear example of the new behavior as it is implemented in our API.
  • Implement the changes in your integration in such a way that it can handle the new behavior and be sure to pass the Asana-Enable header when you make requests. This will ensure that you will always get the new behavior regardless of when the default behavior changes.

At this point, your integration has been migrated to the new behavior. At any point after the end date the Asana-Enable header will be ignored by the Asana API and you can feel free to remove it. (We strongly recommend keeping it all the way through the end date in case of unforseen circumstances that cause us to temporarily reset the default behavior from the new implementation to the deprecated behavior.)

Client library support

The latest version of our official client libraries for Python, Java, PHP, and Javascript all support sending custom headers and are able to use our deprecations framework. Consult the individual libraries for how to send headers with each request.

Active deprecations

Here is a table of recent, ongoing, and upcoming deprecations, including their header names, links to resources, and relevant dates where available. If dates are available, the next date to occur for that change is bolded.

DeprecationHeader nameStart dateActivation dateEnd date
Security headerssecurity_headers2018-08-032018-09-282018-11-15
New rich textnew_rich_text2018-08-072018-10-152018-12-17
New task subtypesnew_task_subtypes2018-10-01See [1]1 mo. after activation
New sectionsnew_sections2019-01-28See [2]See [2]
String IDsstring_ids2019-02-122019-08-132020-02-11

[1] The activation date for this change will be one month after the public launch of the new task subtype. The end date for this change will be two months after the public launch.

[2] This migration is happening simultaneously with the rollout of a change to the Asana web app, and so the timeline for it is subject to change with ongoing development of the web app. Refer to the deprecation details for the most current information.


Why use Asana's API?

Asana’s API and you

Collaboration across teams and tools works best when everyone stays in sync and processes flow easily and without friction. This is why we have Asana's API: it's a platform to ensure all of your information is up to date and that your teams stay efficient and in the loop.

Asana’s API provides a means for software and scripts to read information from inside Asana, input information from outside Asana, and automatically react when things change. This can include:

  • Consistently doing repetitive or tedious tasks.
  • Chaining a process together by responding to changes.
  • Creating reports on the state of tasks and projects.
  • Staying in sync with other software such as Slack or Salesforce used in your organization.
  • Pulling information from other locations like email or Evernote into Asana.
  • Adding new features on top of Asana.
  • Customizing Asana for your team’s processes and workflows.

The role of Asana's platform is to allow Asana to meet your team where you are and how you work. Asana is built to be flexible and powerful, to be intuitive enough for all teams to adopt and maintain clarity on who is doing what by when. Asana’s platform enables you to specialize this flexibility to maximize efficiency. Here are some ideas for what you can build:

Doing repetitive work

Integrations and bots are great for making sure that repetitive tasks are always taken care of. Running a script with Asana's API can quickly take care of work like moving cards between board columns, setting assignees or due dates based on the state of the task, or asking that all custom fields are set before work can begin on a task. This can save time and overhead when trying to keep your projects clear and correct.

Reacting to changes

Workflows with Asana often have a "when this task changes, do something" feel. An example is moving tasks between projects based on subtasks: if one team completes a subtask, move the parent task to the next team's project. Integrations can be built to respond in near-real time to changes in Asana to move work forward to the next step.

Generating reports

Fetching the state of the tasks in a project or for your teammates can unlock the ability to create simple - or complex - metrics around how you are progressing. How many open tasks are there in the project? Who has the most tasks assigned? How often does the due date of each task get shifted back? Our platform enables pulling of data from Asana to make customized metrics to track your work.

Keeping in sync

Teams frequently use a multitude of software tools to accomplish work, from email to asset management to file storage. These specialized tools are often used with colleagues who don't track their work with Asana; and even if they do, keeping all of your tools in sync makes the transitions between tools straightforward to minimize work about work.

Some of our integration partners, like Tray.io, Unito, and Zapier, offer syncing solutions out of the box with common workplace tools, or you could build your own solution in cases where you need more flexibility, such as when connecting to customized or internally-built software.

Capturing work

Asana is built for teamwork and knowing who is doing what by when. Having an easy way to capture information in Asana makes it less likely that work will slip through the cracks. For example, when communicating with people who work in other companies who aren't members of your Asana organization, an integration like we built for Gmail lets you create follow-up tasks with just a few clicks.

Getting information into Asana in a quick and easy way is important for ensuring that you don’t drop the ball. Asana’s platform is a great way to pull information from many channels into Asana with minimum hassle.

Extending Asana

Asana is built to be a tool that works well for all teams, so we build features into our core product that aren't overly opinionated about how you get work done. At the same time, there is opportunity for teams to use Asana in a specialized way or with specific additional features. When there are features that you'd like Asana to have, our platform is a resource to make them happen. As a matter of fact, some of our more successful integrations like Instagantt exist purely to provide additional features to Asana.

Customizing workflows

Integrations or scripts work great to maintain a custom workflow, saving a team member from having to continually pay attention to the state of tasks in Asana.

Whether it's ensuring that custom fields are filled out, tasks are completed, tasks live in the correct board-view column, or automatically assigning tasks at certain stages in your process, integrations can react to changes in Asana to ensure that everyone is up to date. When processes mature around how you get work done, it's a great time to use Asana's platform to make sure everything stays consistent and clear.

Check out some examples of integrations we've built on Asana below:

Learn more or submit an app you've built

Read more on how to get started building on Asana or submit an app to add to our apps page.

Q&A Response

Asana's API enables customization and automation of your organization’s workflow through scripts built to specialize your use of Asana. Using Asana to track your work and leveraging Asana’s API to automate your processes is a powerful combination which can make your team much more efficient. Here's one example of how we do it at Asana.

Tracking timely responses to support questions

Asana’s developer relations team manages technical support for our API through a number of channels: support tickets, questions about our API and integrations forwarded on from our colleagues, the Asana Community's Developer category, Stack Overflow, pull requests and bug reports from open-source GitHub projects like our client libraries, and more. Staying on top of all of these channels can be daunting, but we want our users to reach us however works best for them. At the same time, we want to isolate the noisiness of incoming requests for our colleagues at Asana who are involved with only one channel.

Additionally, the management of the question and answer process, triaging the incoming requests, troubleshooting with our engineers, and measuring our response performance are all internal processes. Even if we have a workflow in place to support our developer relations team, we want the experience for other teams to be easy and lightweight. We want to ensure our coworkers do the right things by default without hindering the consistency of our work and our ability to track progress.

Our solution: automation and reporting through our API to provide consistent management of the whole process.

To do this, we wrote an integration with the following goals in mind:

  • Maintain clarity amongst our teams by tracking work in Asana.
  • Have only one place we have to look to stay in the loop.
  • Ensure that no questions get missed, i.e. a reminder bot.
  • Let our API users know that they've been heard in a timely fashion.
  • Track our performance in remaining responsive.
  • Automate some of the bookkeeping required to maintain a consistent workflow.
  • Separate the specifics of how we track our performance from our colleagues’ workflows.

The script we built does the following for us:

  1. Integrate with external sources to put incoming questions into Asana, one project per channel.
  2. Add question tasks from each incoming project into a single combined project.
  3. Acknowledge a question has been received and begin tracking response times.
  4. Upon first response, complete a task to signal relevant followers that we've reached out.

Maintain focus

We use webhooks to get notified in near-real time when new tasks are created in any of several Asana projects, one per incoming channel. Some of these projects are automatically synced with outside sources, others are available for our coworkers to create tasks in. Keeping tasks in their source channel helps keep us organized for where to go to respond. These projects are what our colleagues follow in order to remain focused on their own channels.

Our script responds to these webhook notifications from each project by adding these tasks into a single "Developer Questions" project. Our developer relations team can then see all outstanding questions about our API in a single place. This is a key part of hitting our service level agreement (SLA) goals: not having to cycle through many projects and channels to see how we're progressing.

Ensure timely responses

Once a question gets added to our Developer Questions project, our integration creates a subtask on it. This signals to our colleagues that we have received the question and will begin to triage and investigate. The subtask is completed when we first respond to our users to inform them that we're investigating. Completion of the question task itself signals that we've achieved a resolution for the person who reached out to us.

Track progress

Our script can generate a simple report to see which questions are still open, how long they’ve been open, and how much time we have left to answer before we miss our service level agreement limits. A simple webpage that the integration creates enables a high level view of what's still in progress and how timely we've been in the past.

Keep the process moving, automatically

Our integration also helps automate some of the routine steps to ensure questions get answered. After a task gets triaged for priority, our integration sets an appropriate due date. It can also set an assignee and followers based on current workload and by matching certain keywords in the task description. If the task approaches its due date and it has not received a response, the script comments on the task to alert us that the question is about to reach our SLA limit. This helps us keep the right people in the loop with minimal overhead and maximum clarity of what needs to be done by when.

By managing this routine and specialized workflow with automation through Asana’s API, our team is more efficient, more effective, and less likely to make a mistake. We know how responsive we've been and can see how we're doing at any time. We're better able to minimize the number of questions which slip through the cracks. The result is better support for outside developers and increased focus on core work, not work about work.

Over time, we've continuously tweaked how our integration behaves to evolve our process, empowering us to adjust and iterate our approach. This is one of the key opportunities that Asana's API provides: ownership and control over how work gets done. Incremental improvements provide the chance to try out new workflows and settle on one that works well for everyone, leading to a more consistent and customized experience of using Asana.

To get started, check out our quick start page for an overview of Asana's API. For support or to generate ideas of how your team can work more effectively with Asana, head to the Asana Community to chat with Asana team members and users!


Bot examples

Reminder bot

Even the most conscientious and best-intentioned teammate can get overloaded and occasionally forget a task. For project managers, team leads, or coordinators, it can be draining to check-in on everyone to make sure that everything is going according to schedule. How can you stay on track and minimize the work-about-work?

Instead of continually reminding teammates to stay focused, use Asana’s API to create a bot for automatic reminders (a bot is a script that performs a task automatically). In this case, a "ping bot" takes action when due dates are approaching (or for any other specified trigger). This can act as a more intelligent version of the reminders that Asana already sends when due dates approach. For example, this persistent friend could comment with reminders further in advance, ask assignees or followers to take some action like setting a custom field, re-assign the work, and/or push out due dates. With a bot taking care of the schedule and reminders, people can spend their time on the work that needs human attention, like ideation and feedback.

Recruiting bot

At Asana, we use a bot to help automate the job application process for engineering candidates. When candidates complete a coding test, the test needs to get graded in a timely manner to keep the interview process on track and to keep the candidate from feeling stranded or forgotten about.

Once candidates have submitted their coding test, a bot uses the Asana API to assign the test to a grader based on specific criteria tracked in Asana, such as their preferred programming languages and number of previous evaluations. Graders are given x days to grade tests (the bot takes into account when graders are out of office). If tests have not been graded by the due date, graders are pinged by the bot with a comment on the task to either grade the test or re-assign it to someone else. After x days, the bot automatically re-assigns the test to the next grader to keep the process moving.

Bugs bot

Our engineering teams handle triaging bug reports by creating a task in a "Bugs" project. A bot then adds the project manager of the relevant team in Asana as a follower, moves the task into a "needs triage" section, and requests assistance. The project manager can then evaluate the bug and triage it.

Since the evaluation of the severity of the bug is important for understanding how urgent the fix is, Bugs Bot will remain persistent, commenting every few days until the task has been moved out of the triage section and into a section of the relevant priority. This process ensures that we're aware of the impact of bugs and helps us avoid severe bugs slipping through the cracks.


How to use the API

Request

https://app.asana.com/api/1.0/users/me

If you're familiar with building against APIs, you can jump to our Quick Start.



If you’re new to developing on APIs, this is a great place to start. You’ll learn in this guide how to make the simplest Asana API request -- getting your user information.

To get started

  1. Be logged into Asana.
  2. Go to this URL: https://app.asana.com/api/1.0/users/me

Response

{
  "data": {
    "gid": "12e345a67b8910c11",
    "email": "jonsnow@example.com",
    "name": "Jon Snow",
    "photo": {
      "image_21x21": "https://s3.amazonaws.com/profile_photos/121110987654321.hJGlskahcKA5hdslf4FS_21x21.png",
      "image_27x27": "https://s3.amazonaws.com/profile_photos/121110987654321.hJGlskahcKA5hdslf4FS_27x27.png",
      "image_36x36": "https://s3.amazonaws.com/profile_photos/121110987654321.hJGlskahcKA5hdslf4FS_36x36.png",
      "image_60x60": "https://s3.amazonaws.com/profile_photos/121110987654321.hJGlskahcKA5hdslf4FS_60x60.png",
      "image_128x128": "https://s3.amazonaws.com/profile_photos/121110987654321.hJGlskahcKA5hdslf4FS_128x128.png"
    },
    "resource_type": "user"
  }
}

And Congratulations! You’ve just made your first Asana API request.



Let’s unpack what just happened. The base URL for all requests to the Asana API is https://app.asana.com/api/1.0. We want information about users, so we go down a level to the https://app.asana.com/api/1.0/users resource. Within the set of all users in Asana, We’re specifically looking to get information about our own account, so we get more specific by adding /me to get https://app.asana.com/api/1.0/users/me. The /me part would ordinarily be an identifier (a long number) or email address of the user, but we've created this shorthand for getting the current user of Asana's API, whomever that may be. Put it together and you have the above path to get your user information from Asana.

Since every API request you make will start with the same base URL ('https://app.asana.com/api/1.0'), we'll just talk about what comes next in the URL -- which is often referred to as a 'resource' or 'path'. For instance, when we say /users/me it's actually shorthand for the entire URL which would be https://app.asana.com/api/1.0/users/me.

After requesting information from the API, you will receive a resposne in JSON format, which can be read and understood by both humans and computers. It's structured in a particular way so programs can rely on a consistent format for the data.

Our API is documented for what resources are available and what sort of return data to expect. For example, here are the docs for the /users endpoint which we just called. This is where you can discover what's possible with our API.

Now, let’s make the same call to /users/me more like software would. Before we do so, we’ll need to get access outside of your web browser to the API.


Authentication Quick Start

Similarly to entering your username/password into a website or logging into Asana with Google, when you access your Asana data via the API you need to authenticate. In the above example, you were already logged into Asana in your browser so you were able to authenticate to the API with credentials stored by your browser.

If you want to write a script that interacts with the Asana API, the easiest method is to get a Personal Access Token (PAT), which you can think of as your unique password for accessing the API.

App or PAT?

If your app needs to perform actions on behalf of users, you should use OAuth.

Getting a Personal Access Token (PAT)

Example PAT

0/68a9e79b868c6789e79a124c30b0
  1. Open the Developer App Management page by using this permalink or following these steps:

* From within Asana, click your profile photo from the top bar and select "My Profile Settings"

* Click the "Apps" tab

* Click "Manage Developer Apps"

  1. Click "+ Create New Personal Access Token"

  2. Type a description of what you’ll use the Personal Access Token for.

  3. Click "Create"

  4. Copy your token. You will only see this one time, but you can always create another PAT later.

Note: treat your PAT like you would a password. Do not share it or display it online.


Accessing the API in the Terminal

curl Request

curl https://app.asana.com/api/1.0/users/me \
  -H "Authorization: Bearer 0/123456789abcdef"

We’ll use cURL, a command line[^1] program to make HTTP requests. MacOS and many Linux distributions have cURL pre-installed, and it’s available for download on Windows and many other operating systems. If you’re curious, you can learn more about cURL in its own documentation.

Let’s do this:

Response

{
  data: {
    gid: "<your gid>",
    email: "<your email>"
    name: "<your name>",
    ...
  }
}
  1. Open a terminal (instructions for Mac and Windows)

  2. Copy/paste the cURL request on the right (make sure to enter your personal access token instead of the placeholder after the word "Bearer", or else you'll get a "Not Authorized" message):

  3. Press Enter

Nice work! You just wrote a cURL command.

In our API documentation, we will often write examples as cURL commands since it's a middle-of-the-road approach to accessing our API: more flexible than using a browser, but user-friendly enough to be quick and easy to do.

You’re ready to start coding!

Asana has client libraries in several popular coding languages. Using these libraries has several advantages (like managing authorization and retrying errors) that make them a good place to go from here. Let’s take a look at making the same /users/me request in Python, JavaScript, and Ruby (feel free to skip ahead to your favorite of the three languages).


Accessing the API with Postman

You can quickly get started on Asana's API with the API Explorer. However, if you want a more robust experience hitting the API, we recommend using Postman. You can get started with the 'Run in Postman' button!

Once you have the collection, you should create an environment.

You'll want to set:

  1. authentication_token to a PAT. If you don't have one yet, visit our Authentication Quick Start
  2. workspace to your workspace's gid, you can find via a logged-in browser by going to https://app.asana.com/api/1.0/users/me/workspaces, or you can hit that endpoint using your PAT
  3. any other gids you want to easily access.
    1. For example, in my Postman. I set task to the gid of a task I regularly test with, project to a the gid of a private sandbox project, and user to the string 'me'.

No need to edit your environment for requests on different objects, simply navigate to the endpoint you want to use, and change the {{object}} to any gid you want.

Importing this collection gives you a snapshot of the API at this time. To stay up to date with API changes, you'll have to re-import the collection by hitting the 'Run in Postman' button, and choosing to override your existing collection. This means if you want to save custom requests, you should do so in a different collection.


Quick Start

Triage Bot

We will use a very basic file structure for the purposes of this guide. Start by making a project directory, moving into it, and running npm init.

$ mkdir triage-bot
$ cd triage-bot
$ npm init

Install the Node Asana client library and create config and app files:

$ npm install asana
$ touch config.js app.js

Add gids (global ids) for the workspace, design request project, and designers that will be assigned requests (note that all gids in Asana should be strings). You can get a project’s gid from its URL in the Asana web product (e.g. the structure of links for a task is www.asana.com/0/{project_gid}/{task_gid}). Similarly, you can get user’s gid from the URL of their task list (i.e. click on their name in Asana). To get your workspace gid(s), go to https://app.asana.com/api/1.0/users/me/workspaces.

let config = {
  workspaceId: "15793206719",
  designRequestProjectId: "180350018127066",
  //gids of designers who are fulfilling design requests
  designers: ["180015866142449", "180015866142454", "180015886142844"]
};

module.exports = config;

Next, let’s get started on our app.js file. Include the Asana node client library and the config file:

let asana = require('asana');
let config = require('./config');

Get your access token and use it to create an Asana client. At the time of writing this guide, the Asana API is going through two deprecations (moving to string gids and changing how sections function). You can learn about our deprecations framework in our docs. To prevent my app from breaking when the deprecations are finalized, I'm passing headers to enable the new API behavior for string gids and sections. We will also set a delay to determine how quickly our parallel requests are sent.

// Get personal access token (PAT) from environment variables.
const accessToken = process.env.triage_bot_pat;

const deprecationHeaders = {"defaultHeaders": {"asana-enable": "new_sections,string_ids"}};

// Create Asana client using PAT and deprecation headers.
const client = asana.Client.create(deprecationHeaders).useAccessToken(accessToken);

// Request delay in ms
const delay = 200;

Use the search API to fetch unassigned tasks from the design requests project. Note that the search API doesn’t return paginated results so you need to pass the max limit which is 100. If there are often more than 100 unassigned tasks, you can add a function to keep running the script until there are no more unassigned tasks.

function getUnassignedTasks() {
  let workspaceId = config.workspaceId;
  let params = {
    "projects.any" : config.designRequestProjectId,
    "assignee.any" : "null",
    "resource_subtype": "default_task",
    "fields": "gid",
    "limit": 100
  };
  client.tasks.searchInWorkspace(workspaceId, params).then(tasks => {
    randomAssigner(tasks.data);
  });
}

We’ll need a few helper functions. One to shuffle an array and another to assign tasks.

function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
      let j = Math.floor(Math.random() * (i + 1));
      let temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
  return array;
}

function assignTask(taskStringId, assigneeStringId) {
  client.tasks.update(taskStringId, {assignee: assigneeStringId})
}

Our final function will take the array of unassigned tasks and round-robin assign them to the group of shuffled designers from the config file. We will use an interval to loop so we can control the speed of the requests. You can change the delay with the const you declared earlier. This is a balance between speed and staying within our concurrent request limit. In node, a normal loop would send all requests at once, which doesn’t work in larger projects.

function randomAssigner(unassignedTasks) {
  let shuffledDesigners = shuffleArray(config.designers);
  let numDesigners = shuffledDesigners.length;

  // Let's use an interval to loop, so we control how quickly requests are sent
  let index = 0;
  let interval = setInterval(function() {
    assignTask(unassignedTasks[index].gid, shuffledDesigners[index % numDesigners]);
    index++;

    // When our index reaches the end, we're done
    if (index >= unassignedTasks.length) {
      clearInterval(interval);

      console.log("You've assigned " + unassignedTasks.length + " new design requests");
    }
  }, delay);
}

Then we just need to call our getUnassignedTasks function to kick-off the script:

getUnassignedTask();

Now run your script, sit back, and watch the bot do your work.

$ node app.js

Why build a bot?

When processes get complex in Asana there can begin to be work about work. This could be happening to you (or someone you love) if you find yourself spending time doing repetitive work such as triaging tasks, reminding people to do something, or adding/removing followers as you move a task through a workflow.

What we’re going to build

In this guide, we will build a simple triage bot that will assign tasks. This is a common Asana use case with support inboxes or request projects.

If you want to skip ahead and see the code for the triage bot, it’s on Github in the Javascript folder of our devrel-examples repo: https://github.com/Asana/devrel-examples.

For this guide, let’s suppose a design team has a requests project where people from the marketing team fill out an Asana form to request graphics from the design team. The form creates a task in the design requests project that needs to be assigned to a designer.

Our triage bot will gather all unassigned tasks in the design request project and then randomly distribute the requests across a group of designers.

For the purposes of this guide, we will keep it this simple, however, you could add more complex logic to your bot. For instance, you could check custom field values on the request task to see what type of request it is (e.g. video, graphic, logo, etc.) and then assign it to the designer that has those skills. You could go even further and check the designers workload to see who currently has the least amount of work already assigned to them (this could be determined by a point value for tasks assigned to them in the project). You could then have the bot ping the design request task as it approaches the due date to ensure that the designer will have it completed on deadline.

Before we get started, here are some helpful links for building on the Asana API:

Create your bot user in Asana

Create a new Asana account for your bot (instructions for inviting users). You want to create a distinct Asana account for your bot because any action it takes in Asana will be attributed to this user. Give your bot a name and photo that will be recognizable to users in Asana that encounter it. Note that if your bot is a guest member in Asana that it will need to be added to every project you need it to work in. Bots based on guest Asana accounts will also not have access to some API features such as defining new custom fields or modifying their settings.

Authenticating your bot

We will authenticate our bot using a Personal Access Token (PAT). Log in to the Asana account that will be used for the bot and navigate to the developer console. You can get to your dev console by either using this URL https://app.asana.com/-/developer_console or from within Asana by clicking your photo icon in the upper right of Asana -> My Profile Settings -> Apps -> Manage Developer Apps.

Next, click “+ New access token” and follow the instructions to get your token. Treat this token like a username and password. Don’t share it with anyone and never publish it to a public repository. I like to save my PAT as an environment variable (here are instructions on how to do this on Mac). For this guide, I’ve saved a PAT as an env variable called triage_bot_pat.

Create an Asana sandbox

Before we start coding, create a project in Asana to use as a sandbox. While not required, I like to set the project to private while developing. To get some users in the project, add your main Asana user as well as your bot account. You could also invite a personal email as a guest user.

Choose an Asana client library

The Asana API has SDKs in several popular languages. For most developers, we recommend using one of our client libraries because they help with some of the complexities of using an API such as authentication, pagination, and deprecations.

For this guide, we will use the Asana Node client library, however, you can follow along in any language of your choice.

Each library has an examples folder in addition to the readme, which can be helpful for getting started. The methods for each resource can be found in the “gen” folder of each client library (e.g. node-asana/lib/resources/gen/).

Let’s start coding!

If you haven't already, follow the code and headers in this section. This will walk you through the terminal commands and code with explainations along the way.

Congratulations! Now go above and beyond

You’ve created an Asana triage bot. Let’s explore a few ideas on how to make it even better.

Deploy your app

While you can run your bot from the command line, that seems like a lot of work to run a bot that’s supposed to eliminate work about work.

One option is to use launchd to automatically execute your script (launchd is like cron but better). Here’s a tutorial to get you started with launchd.

The next step would be to deploy to a hosted server. Here’s a guide exploring some of the popular hosting providers. Hosting your app makes it more resilient and allows you to create more sophisticated apps (e.g. use webhooks).

Use Asana as your config file

To take your bot’s accessibility to the next level, put your configuration in an Asana task(s) and then have your script read that task(s) for instructions. This allows you (or anyone else) to make config changes without touching any code. For example, if a designer is on vacation, you can easily remove them from the group that gets assigned requests. Similarly, if a designer joins or leaves the team, this change could be easily configured in a task instead of having to change the bot’s code.

To see this approach in the wild, checkout Ohmega, an automation framework we created. Here’s the configuration service that reads a tree of tasks for its configuration.

Use webhooks for real-time triaging

If you need your bot to react to changes in real time, then you’ll need to use webhooks. We built a python webhook inspector to help developers get started using Asana webhooks.


Python Hello World

import asana

# replace with your personal access token. 
personal_access_token = '0/123456789....'

# Construct an Asana client
client = asana.Client.access_token(personal_access_token)
# Set things up to send the name of this script to us to show that you succeeded! This is optional.
client.options['client_name'] = "hello_world_python"

# Get your user info
me = client.users.me()

# Print out your information
print "Hello world! " + "My name is " + me['name'] + "!"

To get started, run pip install asana or follow the detailed installation instructions on the GitHub page for the Python client library.

Once it’s installed, open your favorite text editor and we’ll code a GET request to /users/me - the same request as above, but in Python.

Save this file as something descriptive like "hello_world.py"

To run this script in your console, pass it as an argument to the python interpreter, i.e. python hello_world.py from the same directory as the file. You should see the message we wrote above with your user information.

As an aside, for clarity python-asana will also work with Python 3.x (with small changes to the above example to make it compatible.)

All of the built-in functions can be found in the /gen folder of the client library.

You can see a variant of this script, and other useful Asana API scripts, in our open-source Github examples repository


Node Hello World

var asana = require('asana');

// replace with your personal access token. 
var personalAccessToken = '0/123456789....';

// Construct an Asana client
var client = asana.Client.create().useAccessToken(personalAccessToken);

// Get your user info
client.users.me()
  .then(function(me) {
    // Print out your information
    console.log('Hello world! ' + 'My name is ' + me.name + '!');
});

To get started, npm install asana or follow the detailed installation instructions on the GitHub page for the Node client library.

Once it’s installed, open your favorite text editor and we’ll code a GET request to /users/me. - the same request as above, but in JavaScript.

Save this file as something descriptive like "hello_world.js"

To run this script in your console, pass it as an argument to the node interpreter, i.e. node hello_world.py from the same directory as the file. You should see the message we wrote above with your user information.

All of the built-in functions can be found in the /gen folder of the client library.

You can see a variant of this script, and other useful Asana API scripts, in our open-source Github examples repository


Ruby Hello World

require 'asana'

# replace with your personal access token. 

personal_access_token = '0/123456789....'

client = Asana::Client.new do |c|

  c.authentication :access_token, personal_access_token

end

me = client.users.me

puts "Hello world! " + "My name is " + me.name + "!"

To get started, gem install asana or follow the detailed installation instructions on the GitHub page for the Ruby client library.

Once it’s installed, open your favorite text editor and we’ll code a GET request to /users/me.

Save this file as something descriptive like "hello_world.rb"

To run this script in your console, pass it as an argument to the ruby interpreter, i.e. ruby hello_world.rb from the same directory as the file. You should see the message we wrote above with your user information.

All of the built-in methods can be found in the /resources folder of the client library.

You can see a variant of this script, and other useful Asana API scripts, in our open-source Github examples repository


Official Client Libraries

Asana is committed to making the developer experience as smooth as possible. Part of this effort is providing high-quality libraries you can use to access the API. The available libraries are listed below, and more are in development. If you don't see what you need, check the open-source community for your platform, and let us know that you'd like to find one here!


JavaScript (Node)

npm install asana

For use in the Node server-side JavaScript runtime.

Links: GitHub

Installation: Install with npm


JavaScript (Browser)

<script src="asana-min.js"></script>

This is built from the Node library above, so you get the exact same interface.

Links: GitHub

Installation: Visit the releases page to download the latest full or minified JS bundle, then include the script in your web page.


Python

pip install asana

Links: GitHub

Installation: Install with pip


Ruby

gem install asana

Links: GitHub

Installation: Install with gem


Java

<dependency>
  <groupId>com.asana</groupId>
  <artifactId>asana</artifactId>
  <version>0.6.0</version>
</dependency>

Links: GitHub

Installation: If you use Maven for dependency management simply include the following in your pom.xml.


PHP

"require": {
  "asana/asana": "^0.1.2"
}

Links: GitHub

Installation: If you use Composer to manage dependencies you can include the "asana/asana" package as a dependency.


API Reference

Scroll down for code samples, example requests and responses. Select a language for code samples from the tabs above or the mobile navigation menu.

This is the interface for interacting with the Asana Platform. Our API reference is generated from our OpenAPI spec.

Base URLs:

Terms of service Web: Asana Support License: Apache 2.0


Attachments

GET /attachments/{attachment_gid}
DELETE /attachments/{attachment_gid}
GET /tasks/{task_gid}/attachments
POST /tasks/{task_gid}/attachments

An attachment object represents any file attached to a task in Asana, whether it’s an uploaded file or one associated via a third-party service such as Dropbox or Google Drive.


Get an attachment

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/attachments/{attachment_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "attachment",
    "name": "Screenshot.png",
    "created_at": "2012-02-22T02:06:58.147Z",
    "download_url": "https://www.dropbox.com/s/123/Screenshot.png?dl=1",
    "host": "dropbox",
    "parent": {
      "gid": "12345",
      "resource_type": "task",
      "name": "Bug Task"
    },
    "view_url": "https://www.dropbox.com/s/123/Screenshot.png"
  }
}

GET /attachments/{attachment_gid}

Get the full record for a single attachment.

Parameters

NameDescription
/attachment_gid string
required
Globally unique identifier for the attachment.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 AttachmentSuccessfully retrieved the record for a single attachment.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
402 NoneThe request was valid, but the queried object or object mutation specified in the request is above your current premium level.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
424 ErrorYou have exceeded one of the enforced rate limits in the API. See the documentation on rate limiting for more information.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
501 ErrorThere is an issue between the load balancers and Asana's API.
503 ErrorEither the upstream service is unavailable to the API, or he API has been intentionally shut off.
504 ErrorThis request took too long to complete.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Delete an attachment

Code samples

# You can also use wget
curl -X DELETE https://app.asana.com/api/1.0/attachments/{attachment_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {}
}

DELETE /attachments/{attachment_gid}

Deletes a specific, existing attachment.

Returns an empty data record.

Parameters

NameDescription
/attachment_gid string
required
Globally unique identifier for the attachment.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully deleted the specified attachment.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get attachments for a task

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/tasks/{task_gid}/attachments \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "attachment",
      "name": "Screenshot.png"
    }
  ]
}

GET /tasks/{task_gid}/attachments

Returns the compact records for all attachments on the task.

Parameters

NameDescription
/task_gid string
required
The task to operate on.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 AttachmentSuccessfully retrieved the compact records for all attachments on the task.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Upload an attachment

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/tasks/{task_gid}/attachments \
  -H 'Content-Type: multipart/form-data' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

file: string

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "attachment",
    "name": "Screenshot.png",
    "created_at": "2012-02-22T02:06:58.147Z",
    "download_url": "https://www.dropbox.com/s/123/Screenshot.png?dl=1",
    "host": "dropbox",
    "parent": {
      "gid": "12345",
      "resource_type": "task",
      "name": "Bug Task"
    },
    "view_url": "https://www.dropbox.com/s/123/Screenshot.png"
  }
}

POST /tasks/{task_gid}/attachments

Upload an attachment.

This method uploads an attachment to a task and returns the compact record for the created attachment object. It is not possible to attach files from third party services such as Dropbox, Box & Google Drive via the API. You must download the file content first and then upload it as any other attachment.

The 100MB size limit on attachments in Asana is enforced on this endpoint.

This endpoint expects a multipart/form-data encoded request containing the full contents of the file to be uploaded.

Requests made should follow the HTTP/1.1 specification that line terminators are of the form CRLF or \r\n outlined here in order for the server to reliably and properly handle the request.

Parameters

NameDescription
/task_gid string
required
The task to operate on.
body object
required
The file you want to upload.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Detailed descriptions

body: The file you want to upload.

Note when using curl:

Be sure to add an ‘@’ before the file path, and use the —form option instead of the -d option.

When uploading PDFs with curl, force the content-type to be pdf by appending the content type to the file path: —form “file=@file.pdf;type=application/pdf”.

Responses

StatusDescription
200 AttachmentSuccessfully uploaded the attachment to the task.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Batch API

POST /batch

There are many cases where you want to accomplish a variety of work in the Asana API but want to minimize the number of HTTP requests you make. For example:

  • Modern browsers limit the number of requests that a single web page can make at once.
  • Mobile apps will use more battery life to keep the cellular radio on when making a series of requests.
  • There is an overhead cost to developing software that can make multiple requests in parallel.
  • Some cloud platforms handle parallelism poorly, or disallow it entirely.

To make development easier in these use cases, Asana provides a batch API that enables developers to perform multiple “actions” by making only a single HTTP request.

Making a Batch Request

To make a batch request, send a POST request to /batch. Like other POST endpoints, the body should contain a data envelope. Inside this envelope should be a single actions field, containing a list of “action” objects. Each action represents a standard request to an existing endpoint in the Asana API.

The maximum number of actions allowed in a single batch request is 10. Making a batch request with no actions in it will result in a 400 Bad Request.

When the batch API receives the list of actions to execute, it will dispatch those actions to the already-implemented endpoints specified by the relative_path and method for each action. This happens in parallel, so all actions in the request will be processed simultaneously. There is no guarantee of the execution order for these actions, nor is there a way to use the output of one action as the input of another action (such as creating a task and then commenting on it).

The response to the batch request will contain (within the data envelope) a list of result objects, one for each action. The results are guaranteed to be in the same order as the actions in the request, e.g., the first result in the response corresponds to the first action in the request.

The batch API will always attempt to return a 200 Success response with individual result objects for each individual action in the request. Only in certain cases (such as missing authorization or malformed JSON in the body) will the entire request fail with another status code. Even if every individual action in the request fails, the batch API will still return a 200 Success response, and each result object in the response will contain the errors encountered with each action.

Rate Limiting

The batch API fully respects all of our rate limiting. This means that a batch request counts against both the standard rate limiter and the concurrent request limiter as though you had made a separate HTTP request for every individual action. For example, a batch request with five actions counts as five separate requests in the standard rate limiter, and counts as five concurrent requests in the concurrent request limiter. The batch request itself incurs no cost.

If any of the actions in a batch request would exceed any of the enforced limits, the entire request will fail with a 429 Too Many Requests error. This is to prevent the unpredictability of which actions might succeed if not all of them could succeed.

Restrictions

Not every endpoint can be accessed through the batch API. Specifically, the following actions cannot be taken and will result in a 400 Bad Request for that action:

  • Uploading attachments
  • Creating, getting, or deleting organization exports
  • Any SCIM operations
  • Nested calls to the batch API


Submit parallel requests

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/batch \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "actions": [
      {
        "relative_path": "/tasks/123",
        "method": "get",
        "data": {
          "assignee": "me",
          "workspace": "1337"
        },
        "options": {
          "limit": 3,
          "fields": [
            "name",
            "notes",
            "completed"
          ]
        }
      }
    ]
  }
}

200 Response

{
  "data": [
    {
      "status_code": 200,
      "headers": {
        "location": "/tasks/1234"
      },
      "body": {
        "data": {
          "gid": "1967",
          "completed": false,
          "name": "Hello, world!",
          "notes": "How are you today?"
        }
      }
    }
  ]
}

POST /batch

Make multiple requests in parallel to Asana's API.

Parameters

NameDescription
body object
required
The requests to batch together via the Batch API.
data objectnone
actions [BatchRequest][A request object for use in a batch request.]
relative_path string
required
The path of the desired endpoint relative to the API’s base URL. Query parameters are not accepted here; put them in data instead.
method string
required
The HTTP method you wish to emulate for the action.
data objectFor GET requests, this should be a map of query parameters you would have normally passed in the URL. Options and pagination are not accepted here; put them in options instead. For POST, PATCH, and PUT methods, this should be the content you would have normally put in the data field of the body.
options objectPagination (limit and offset) and output options (fields or expand) for the action. “Pretty” JSON output is not an available option on individual actions; if you want pretty output, specify that option on the parent request.
limit integerPagination limit for the request.
offset integerPagination offset for the request.
fields [string]The fields to retrieve in the request.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Enumerated Values

ParameterValue
methodget
methodpost
methodput
methoddelete
methodpatch
methodhead

Responses

StatusDescription
200 BatchResultSuccessfully completed the requested batch API operations.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Custom Fields

POST /custom_fields
GET /custom_fields/{custom_field_gid}
PUT /custom_fields/{custom_field_gid}
DELETE /custom_fields/{custom_field_gid}
GET /workspaces/{workspace_gid}/custom_fields
POST /custom_fields/{custom_field_gid}/enum_options
POST /custom_fields/{custom_field_gid}/enum_options/insert
PUT /enum_options/{enum_option_gid}

In the Asana application, Tasks, Projects, and Portfolios can hold user-specified Custom Fields which provide extra information; for example, a priority value or a number representing the time required to complete a Task. This lets a user define the type of information that each Item within a Project or Portfolio can contain in addition to the built-in fields that Asana provides.

Note: Custom Fields are a premium feature. Integrations which work with Custom Fields need to handle an assortment of use cases for free and premium users in context of free and premium organizations. For a detailed examination of to what data users will have access in different circumstances, read the section below on access control.

The characteristics of Custom Fields are:

  • There is metadata that defines the Custom Field. This metadata can be shared across an entire workspace, or be specific to a Project or Portfolio. * Creating a Custom Field Setting on a Project or Portfolio means each direct child will have the custom field. This is conceptually akin to adding columns in a database or a spreadsheet: every Task (row) in the Project (table) can contain information for that field, including "blank" values, i.e. null data. For Portfolio custom fields, every Project (row) in the Portfolio (table) will contain information for the custom field. * Custom Field Settings only go one child deep. Meaning a custom field setting on a portfolio will give each project the custom field, but not each task within those projects. * Tasks have Custom Field values assigned to them.

A brief example: let's imagine that an organization has defined a Custom Field for "Priority". This field is of enum type and can have user-defined values of Low, Medium, or High. This is the field metadata, and it is visible within, and shared across, the entire organization.

A Project is then created in the organization, called "Bugs", and the "Priority" Custom Field is associated with that Project. This will allow all Tasks within the "Bugs" Project to have an associated "Priority".

A new Task is created within "Bugs". This Task, then, has a field named "Priority" which can take on the Custom Field value of one of [null], Low, Medium, and High.

These Custom Fields are accessible via the API through a number of endpoints at the top level (e.g. /custom_fields and /custom_field_settings) and through calls on Workspaces, Portfolios, Projects, and Tasks resources. The API also provides a way to fetch both the metadata and data which define each particular Custom Field, so that a client application may render proper UI to display or edit the values.

Custom Field aware integrations need to be aware of the basic types that Custom Fields can adopt. These types are:

  • text - an arbitrary, relatively short string of text * number - a number with a defined level of precision * enum - a selection from a defined list of options

Text fields are currently limited to 1024 characters. On Tasks, their Custom Field value will have a text_value property to represent this field.

Number fields can have an arbitrary precision associated with them; for example, a precision of 2 would round its value to the second (hundredths) place, i.e. 1.2345 would round to 1.23. On Tasks, the Custom Field value will have a number_value property to represent this field.

Enum fields represent a selection from a list of options. On the metadata, they will contain all of the options in an array. Each option has 4 properties:

  • gid - the gid of this enum option. Note that this is the gid of the option - the Custom Field itself has a separate gid. * name - the name of the option, e.g. "Choice #1" * enabled - whether this field is enabled. Disabled fields are not available to choose from when disabled, and are visually hidden in the Asana application, but they remain in the metadata for Custom Field values which were set to the option before the option was disabled. * color - a color associated with this choice.

On the Task's Custom Field value, the enum will have an enum_value property which will be the same as one of the choices from the list defined in the Custom Field metadata.

Querying an organization for its Custom Fields

For Custom Fields shared across the workspace or organization, the Workspace can be queried for its list of defined Custom Fields. Like other collection queries, the fields will be returned as a compact record; slightly different from most other compact records is the fact that the compact record for Custom Fields includes type as well as gid and name.

Accessing Custom Field definitions

The Custom Fields reference describes how the metadata which defines a Custom Field is accessed. A GET request with a gid can be issued on the /custom_fields endpoint to fetch the full definition of a single Custom Field given its gid from (for instance) listing all Custom Fields on a Workspace, or getting the gid from a Custom Field Settings object or a Task.

Associating Custom Fields with a Project or Portfolio

A mapping between a Custom Field and a Project or Portfolio is handled with a Custom Field Settings object. This object contains a reference for each of the Custom Field and the Project or Porfolio, as well as additional information about the status of that particular Custom Field. For instance, is_important, which defines whether or not the custom field will appear in the list/grid on the Asana application.

Accessing Custom Field values on Tasks or Projects

The Tasks reference has information on how Custom Fields look on Tasks. Custom Fields will return as an array on the property custom_fields, and each entry will contain, side-by-side, the compact representation of the Custom Field metadata and a {typename}_value property that stores the value set for the Custom Field.

Of particular note is that the top-level gid of each entry in the custom_fields array is the gid of the Custom Field metadata, as it is the compact representation of this metadata. This can be used to refer to the full metadata by making a request to the /custom_fields/{custom_fields_id} endpoint as described above.

Custom Fields can be set just as in the Asana-defined fields on a task via POST or PUT requests. You can see an example on the update a task endpoint.

Custom Fields on projects follow this same pattern.

Warning: Program defensively with regards to Custom Field definitions

Asana application users have the ability to change the definitions of Custom Field metadata. This means that as you write scripts or applications to work with them, it's possible for the definitions to change at any time, which may cause an application using them to break or malfunction if it makes assumptions about the metadata for a particular Custom Field. When using Custom Fields, it is a good idea to program defensively, meaning you your application should double-check that the Custom Field metadata is what it expects.

Storing the state of the Custom Field metadata for too long if you dynamically create a model for it can cause your model to become unsynchronized with the model stored in Asana. If you encounter (for example) an enum value on a Task that does not match any option in your metadata model, your metadata model has become out of date with the Custom Field metadata.

Note: We are currently studying proposals for future implementations to more elegantly handle the modification of Custom Field metadata for application integrations.

Enabled and Disabled Values

When information that is contained in a Custom Field value loses a logical association with its metadata definition, the value becomes disabled. This can happen in a couple of simple ways, for example, if you remove the Custom Field metadata from a Project, or move a Task with a Custom Field to a different Project which does not have the Custom Field metadata associated with it. The value remains on the Task, and the Custom Field metadata can still be found and examined, but as the context in which the Custom Field makes sense is gone, the Custom Field cannot change its value; it can only be cleared.

Note: Tasks that are associated with multiple Projects do not become disabled, so long as at least one of the Projects is still associated with the Custom Field metadata. In other words, Tasks with multiple Projects will retain logically associated to the set of Custom Field metadata represented by all of their Projects.

Moving the Task back under a Project with that Custom Field applied to it or applying the Custom Field metadata to the current Project will return the Custom Field value to an enabled state. In this scenario, the Custom Field will be re-enabled and editable again.

In the Asana application, disabled fields are grayed out and not allowed to change, other than to be discarded. In the API, we return a property enabled: false to inform the external application that the value has been disabled.

Note that the API enforces the same operations on disabled Custom Field values as hold in the Asana application: they may not have their values changed, since the lack of context for the values of a custom field in general doesn't provide enough information to know what new values should be. Setting the Custom Field value to null will clear and remove the Custom Field value from the Task.

Custom Field access control

Custom Fields are a complex feature of the Asana platform, and their access in the Asana application and in the API vary based on the status of the user and project. When building your application, it's best to be defensive and not assume the given user will have read or write access to a custom field, and fail gracefully when this occurs.


Create a custom field

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/custom_fields \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Bug Task",
    "resource_subtype": "text",
    "enum_options": [
      {
        "name": "Low",
        "enabled": true,
        "color": "blue"
      }
    ],
    "enum_value": {
      "name": "Low",
      "enabled": true,
      "color": "blue"
    },
    "enabled": true,
    "text_value": "Some Value",
    "description": "Development team priority",
    "precision": 2,
    "has_notifications_enabled": true,
    "workspace": "1331"
  }
}

201 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "custom_field",
    "name": "Bug Task",
    "resource_subtype": "text",
    "type": "text",
    "enum_options": [
      {
        "gid": "12345",
        "resource_type": "enum_option",
        "name": "Low",
        "enabled": true,
        "color": "blue"
      }
    ],
    "enum_value": {
      "gid": "12345",
      "resource_type": "enum_option",
      "name": "Low",
      "enabled": true,
      "color": "blue"
    },
    "enabled": true,
    "text_value": "Some Value",
    "description": "Development team priority",
    "precision": 2,
    "is_global_to_workspace": true,
    "has_notifications_enabled": true
  }
}

POST /custom_fields

Creates a new custom field in a workspace. Every custom field is required to be created in a specific workspace, and this workspace cannot be changed once set.

A custom field’s name must be unique within a workspace and not conflict with names of existing task properties such as ‘Due Date’ or ‘Assignee’. A custom field’s type must be one of ‘text’, ‘enum’, or ‘number’.

Returns the full record of the newly created custom field.

Parameters

NameDescription
body objectThe custom field object to create.
data objectCustom Fields store the metadata that is used in order to add user-specified information to tasks in Asana. Be sure to reference the Custom Fields developer documentation for more information about how custom fields relate to various resources in Asana.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
resource_subtype stringThe type of the custom field. Must be one of the given values.
type stringDeprecated: new integrations should prefer the resource_subtype field. The type of the custom field. Must be one of the given values.
enum_options [object]Conditional. Only relevant for custom fields of type enum. This array specifies the possible values which an enum custom field can adopt. To modify the enum options, refer to working with enum options.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enum_value objectConditional. Only relevant for custom fields of type enum. This object is the chosen value of an enum custom field.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enabled booleanConditional. Determines if the custom field is enabled or not.
text_value stringConditional. This string is the value of a text custom field.
description stringOpt In. The description of the custom field.
precision integerOnly relevant for custom fields of type ‘Number’. This field dictates the number of places after the decimal to round to, i.e. 0 is integer values, 1 rounds to the nearest tenth, and so on. Must be between 0 and 6, inclusive.
is_global_to_workspace booleanThis flag describes whether this custom field is available to every container in the workspace. Before project-specific custom fields, this field was always true.
has_notifications_enabled booleanThis flag describes whether a follower of a task with this field should receive inbox notifications from changes to this field.
workspace string
required
The workspace to create a custom field in.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Enumerated Values

ParameterValue
resource_subtypetext
resource_subtypeenum
resource_subtypenumber
typetext
typeenum
typenumber

Responses

StatusDescription
201 CustomFieldCustom field successfully created.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get a custom field

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/custom_fields/{custom_field_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "custom_field",
    "name": "Bug Task",
    "resource_subtype": "text",
    "type": "text",
    "enum_options": [
      {
        "gid": "12345",
        "resource_type": "enum_option",
        "name": "Low",
        "enabled": true,
        "color": "blue"
      }
    ],
    "enum_value": {
      "gid": "12345",
      "resource_type": "enum_option",
      "name": "Low",
      "enabled": true,
      "color": "blue"
    },
    "enabled": true,
    "text_value": "Some Value",
    "description": "Development team priority",
    "precision": 2,
    "is_global_to_workspace": true,
    "has_notifications_enabled": true
  }
}

GET /custom_fields/{custom_field_gid}

Get the complete definition of a custom field’s metadata.

Since custom fields can be defined for one of a number of types, and these types have different data and behaviors, there are fields that are relevant to a particular type. For instance, as noted above, enum_options is only relevant for the enum type and defines the set of choices that the enum could represent. The examples below show some of these type-specific custom field definitions.

Parameters

NameDescription
/custom_field_gid string
required
Globally unique identifier for the custom field.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 CustomFieldSuccessfully retrieved the complete definition of a custom field’s metadata.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Update a custom field

Code samples

# You can also use wget
curl -X PUT https://app.asana.com/api/1.0/custom_fields/{custom_field_gid} \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Bug Task",
    "resource_subtype": "text",
    "enum_options": [
      {
        "name": "Low",
        "enabled": true,
        "color": "blue"
      }
    ],
    "enum_value": {
      "name": "Low",
      "enabled": true,
      "color": "blue"
    },
    "enabled": true,
    "text_value": "Some Value",
    "description": "Development team priority",
    "precision": 2,
    "has_notifications_enabled": true
  }
}

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "custom_field",
    "name": "Bug Task",
    "resource_subtype": "text",
    "type": "text",
    "enum_options": [
      {
        "gid": "12345",
        "resource_type": "enum_option",
        "name": "Low",
        "enabled": true,
        "color": "blue"
      }
    ],
    "enum_value": {
      "gid": "12345",
      "resource_type": "enum_option",
      "name": "Low",
      "enabled": true,
      "color": "blue"
    },
    "enabled": true,
    "text_value": "Some Value",
    "description": "Development team priority",
    "precision": 2,
    "is_global_to_workspace": true,
    "has_notifications_enabled": true
  }
}

PUT /custom_fields/{custom_field_gid}

A specific, existing custom field can be updated by making a PUT request on the URL for that custom field. Only the fields provided in the data block will be updated; any unspecified fields will remain unchanged When using this method, it is best to specify only those fields you wish to change, or else you may overwrite changes made by another user since you last retrieved the custom field. A custom field’s type cannot be updated. An enum custom field’s enum_options cannot be updated with this endpoint. Instead see “Work With Enum Options” for information on how to update enum_options. Locked custom fields can only be updated by the user who locked the field. Returns the complete updated custom field record.

Parameters

NameDescription
/custom_field_gid string
required
Globally unique identifier for the custom field.
body objectThe custom field object with all updated properties.
data objectCustom Fields store the metadata that is used in order to add user-specified information to tasks in Asana. Be sure to reference the Custom Fields developer documentation for more information about how custom fields relate to various resources in Asana.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
resource_subtype stringThe type of the custom field. Must be one of the given values.
type stringDeprecated: new integrations should prefer the resource_subtype field. The type of the custom field. Must be one of the given values.
enum_options [object]Conditional. Only relevant for custom fields of type enum. This array specifies the possible values which an enum custom field can adopt. To modify the enum options, refer to working with enum options.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enum_value objectConditional. Only relevant for custom fields of type enum. This object is the chosen value of an enum custom field.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enabled booleanConditional. Determines if the custom field is enabled or not.
text_value stringConditional. This string is the value of a text custom field.
description stringOpt In. The description of the custom field.
precision integerOnly relevant for custom fields of type ‘Number’. This field dictates the number of places after the decimal to round to, i.e. 0 is integer values, 1 rounds to the nearest tenth, and so on. Must be between 0 and 6, inclusive.
is_global_to_workspace booleanThis flag describes whether this custom field is available to every container in the workspace. Before project-specific custom fields, this field was always true.
has_notifications_enabled booleanThis flag describes whether a follower of a task with this field should receive inbox notifications from changes to this field.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Enumerated Values

ParameterValue
resource_subtypetext
resource_subtypeenum
resource_subtypenumber
typetext
typeenum
typenumber

Responses

StatusDescription
200 CustomFieldThe custom field was successfully updated.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Delete a custom field

Code samples

# You can also use wget
curl -X DELETE https://app.asana.com/api/1.0/custom_fields/{custom_field_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {}
}

DELETE /custom_fields/{custom_field_gid}

A specific, existing custom field can be deleted by making a DELETE request on the URL for that custom field. Locked custom fields can only be deleted by the user who locked the field. Returns an empty data record.

Parameters

NameDescription
/custom_field_gid string
required
Globally unique identifier for the custom field.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptyThe custom field was successfully deleted.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get a workspace's custom fields

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/workspaces/{workspace_gid}/custom_fields \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "custom_field",
      "name": "Bug Task",
      "resource_subtype": "text",
      "type": "text",
      "enum_options": [
        {
          "gid": "12345",
          "resource_type": "enum_option",
          "name": "Low",
          "enabled": true,
          "color": "blue"
        }
      ],
      "enum_value": {
        "gid": "12345",
        "resource_type": "enum_option",
        "name": "Low",
        "enabled": true,
        "color": "blue"
      },
      "enabled": true,
      "text_value": "Some Value"
    }
  ]
}

GET /workspaces/{workspace_gid}/custom_fields

Returns a list of the compact representation of all of the custom fields in a workspace.

Parameters

NameDescription
/workspace_gid string
required
Globally unique identifier for the workspace or organization.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 CustomFieldSuccessfully retrieved all custom fields for the given workspace.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Create an enum option

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/custom_fields/{custom_field_gid}/enum_options \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Low",
    "enabled": true,
    "color": "blue",
    "insert_before": "12345",
    "insert_after": "12345"
  }
}

201 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "enum_option",
    "name": "Low",
    "enabled": true,
    "color": "blue"
  }
}

POST /custom_fields/{custom_field_gid}/enum_options

Creates an enum option and adds it to this custom field’s list of enum options. A custom field can have at most 50 enum options (including disabled options). By default new enum options are inserted at the end of a custom field’s list. Locked custom fields can only have enum options added by the user who locked the field. Returns the full record of the newly created enum option.

Parameters

NameDescription
/custom_field_gid string
required
Globally unique identifier for the custom field.
body objectThe enum option object to create.
data objectEnum options are the possible values which an enum custom field can adopt. An enum custom field must contain at least 1 enum option but no more than 50.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
insert_before stringAn existing enum option within this custom field before which the new enum option should be inserted. Cannot be provided together with after_enum_option.
insert_after stringAn existing enum option within this custom field after which the new enum option should be inserted. Cannot be provided together with before_enum_option.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
201 EnumOptionCustom field enum option successfully created.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Reorder a custom field's enum

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/custom_fields/{custom_field_gid}/enum_options/insert \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Low",
    "enabled": true,
    "color": "blue",
    "enum_option": "97285",
    "before_enum_option": "12345",
    "after_enum_option": "12345"
  }
}

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "enum_option",
    "name": "Low",
    "enabled": true,
    "color": "blue"
  }
}

POST /custom_fields/{custom_field_gid}/enum_options/insert

Moves a particular enum option to be either before or after another specified enum option in the custom field. Locked custom fields can only be reordered by the user who locked the field.

Parameters

NameDescription
/custom_field_gid string
required
Globally unique identifier for the custom field.
body objectThe enum option object to create.
data objectEnum options are the possible values which an enum custom field can adopt. An enum custom field must contain at least 1 enum option but no more than 50.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enum_option string
required
The gid of the enum option to relocate.
before_enum_option stringAn existing enum option within this custom field before which the new enum option should be inserted. Cannot be provided together with after_enum_option.
after_enum_option stringAn existing enum option within this custom field after which the new enum option should be inserted. Cannot be provided together with before_enum_option.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EnumOptionCustom field enum option successfully reordered.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Update an enum option

Code samples

# You can also use wget
curl -X PUT https://app.asana.com/api/1.0/enum_options/{enum_option_gid} \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Low",
    "enabled": true,
    "color": "blue"
  }
}

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "enum_option",
    "name": "Low",
    "enabled": true,
    "color": "blue"
  }
}

PUT /enum_options/{enum_option_gid}

Updates an existing enum option. Enum custom fields require at least one enabled enum option. Locked custom fields can only be updated by the user who locked the field. Returns the full record of the updated enum option.

Parameters

NameDescription
/enum_option_gid stringGlobally unique identifier for the enum option.
body objectThe enum option object to update
data objectEnum options are the possible values which an enum custom field can adopt. An enum custom field must contain at least 1 enum option but no more than 50.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EnumOptionSuccessfully updated the specified custom field enum.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Custom Field Settings

GET /projects/{project_gid}/custom_field_settings
GET /portfolios/{portfolio_gid}/custom_field_settings
POST /portfolios/{portfolio_gid}/addCustomFieldSetting
POST /portfolios/{portfolio_gid}/removeCustomFieldSetting

Custom fields are attached to a particular project with the Custom Field Settings resource. This resource both represents the many-to-many join of the Custom Field and Project as well as stores information that is relevant to that particular pairing; for instance, the is_important property determines some possible application-specific handling of that custom field.


Get a project's custom fields

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/projects/{project_gid}/custom_field_settings \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "custom_field_setting",
      "project": {
        "gid": "12345",
        "resource_type": "project",
        "name": "Stuff to buy"
      },
      "is_important": false,
      "parent": {
        "gid": "12345",
        "resource_type": "project",
        "name": "Stuff to buy"
      },
      "custom_field": {
        "gid": "12345",
        "resource_type": "custom_field",
        "name": "Bug Task",
        "resource_subtype": "text",
        "type": "text",
        "enum_options": [
          {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          }
        ],
        "enum_value": {
          "gid": "12345",
          "resource_type": "enum_option",
          "name": "Low",
          "enabled": true,
          "color": "blue"
        },
        "enabled": true,
        "text_value": "Some Value",
        "description": "Development team priority",
        "precision": 2,
        "is_global_to_workspace": true,
        "has_notifications_enabled": true
      }
    }
  ]
}

GET /projects/{project_gid}/custom_field_settings

Returns a list of all of the custom fields settings on a project, in compact form. Note that, as in all queries to collections which return compact representation, opt_fields can be used to include more data than is returned in the compact representation. See the getting started guide on input/output options for more information.

Parameters

NameDescription
/project_gid string
required
Globally unique identifier for the project.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 CustomFieldSettingSuccessfully retrieved custom field settings objects for a project.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get a portfolio's custom fields

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/portfolios/{portfolio_gid}/custom_field_settings \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "custom_field_setting",
      "project": {
        "gid": "12345",
        "resource_type": "project",
        "name": "Stuff to buy"
      },
      "is_important": false,
      "parent": {
        "gid": "12345",
        "resource_type": "project",
        "name": "Stuff to buy"
      },
      "custom_field": {
        "gid": "12345",
        "resource_type": "custom_field",
        "name": "Bug Task",
        "resource_subtype": "text",
        "type": "text",
        "enum_options": [
          {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          }
        ],
        "enum_value": {
          "gid": "12345",
          "resource_type": "enum_option",
          "name": "Low",
          "enabled": true,
          "color": "blue"
        },
        "enabled": true,
        "text_value": "Some Value",
        "description": "Development team priority",
        "precision": 2,
        "is_global_to_workspace": true,
        "has_notifications_enabled": true
      }
    }
  ]
}

GET /portfolios/{portfolio_gid}/custom_field_settings

Returns a list of all of the custom fields settings on a portfolio, in compact form.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 CustomFieldSettingSuccessfully retrieved custom field settings objects for a portfolio.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Add a custom field to a portfolio

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/portfolios/{portfolio_gid}/addCustomFieldSetting \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "custom_field": "14916",
  "is_important": true,
  "insert_before": "1331",
  "insert_after": "1331"
}

200 Response

{
  "data": {}
}

POST /portfolios/{portfolio_gid}/addCustomFieldSetting

Custom fields are associated with portfolios by way of custom field settings. This method creates a setting for the portfolio.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
body object
required
Information about the custom field setting.
custom_field string
required
The custom field to associate with this portfolio.
is_important booleanWhether this field should be considered important to this portfolio (for instance, to display in the list view of items in the portfolio).
insert_before stringAn id of a Custom Field Setting on this portfolio, before which the new Custom Field Setting will be added. insert_before and insert_after parameters cannot both be specified.
insert_after stringAn id of a Custom Field Setting on this portfolio, after which the new Custom Field Setting will be added. insert_before and insert_after parameters cannot both be specified.
?opt_pretty booleanProvides “pretty” output.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully added the custom field to the portfolio.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Remove a custom field from a portfolio

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/portfolios/{portfolio_gid}/removeCustomFieldSetting \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "custom_field": "14916"
}

200 Response

{
  "data": {}
}

POST /portfolios/{portfolio_gid}/removeCustomFieldSetting

Removes a custom field setting from a portfolio.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
body object
required
Information about the custom field setting being removed.
custom_field string
required
The custom field to remove from this portfolio.
?opt_pretty booleanProvides “pretty” output.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully removed the custom field from the portfolio.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Events

GET /events

An event is an object representing a change to a resource that was observed by an event subscription.

In general, requesting events on a resource is faster and subject to higher rate limits than requesting the resource itself. Additionally, change events bubble up - listening to events on a project would include when stories are added to tasks in the project, even on subtasks.

Establish an initial sync token by making a request with no sync token. The response will be a 412 error - the same as if the sync token had expired.

Subsequent requests should always provide the sync token from the immediately preceding call.

Sync tokens may not be valid if you attempt to go ‘backward’ in the history by requesting previous tokens, though re-requesting the current sync token is generally safe, and will always return the same results.

When you receive a 412 Precondition Failed error, it means that the sync token is either invalid or expired. If you are attempting to keep a set of data in sync, this signals you may need to re-crawl the data.

Sync tokens always expire after 24 hours, but may expire sooner, depending on load on the service.


Get events on a resource

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/events?resource=12345 \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "user": {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      },
      "resource": {
        "gid": "12345",
        "name": "Bug Task"
      },
      "type": "task",
      "action": "changed",
      "parent": {
        "gid": "12345",
        "resource_type": "task",
        "name": "Bug Task"
      },
      "created_at": "2012-02-22T02:06:58.147Z"
    }
  ],
  "sync": "de4774f6915eae04714ca93bb2f5ee81"
}

GET /events

Returns the full record for all events that have occurred since the sync token was created.

A GET request to the endpoint /[path_to_resource]/events can be made in lieu of including the resource ID in the data for the request.

Parameters

NameDescription
?resource string
required
A resource ID to subscribe to. The resource can be a task or project.
?sync stringA sync token received from the last request, or none on first sync. Events will be returned from the point in time that the sync token was generated.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Detailed descriptions

sync: A sync token received from the last request, or none on first sync. Events will be returned from the point in time that the sync token was generated. Note: On your first request, omit the sync token. The response will be the same as for an expired sync token, and will include a new valid sync token.If the sync token is too old (which may happen from time to time) the API will return a 412 Precondition Failed error, and include a fresh sync token in the response.

Responses

StatusDescription
200 EventSuccessfully retrieved events.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Jobs

GET /jobs/{job_gid}

Jobs represent processes that handle asynchronous work. Jobs are created when an endpoint requests an action that will be handled asynchronously. Such as project or task duplication. Only the creator of the duplication process can access the duplication status of the new object.


Get a job by id

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/jobs/{job_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "task",
    "resource_subtype": "duplicate_task",
    "status": "in_progress",
    "new_project": {
      "gid": "12345",
      "resource_type": "project",
      "name": "Stuff to buy"
    },
    "new_task": {
      "gid": "12345",
      "resource_type": "task",
      "name": "Bug Task"
    }
  }
}

GET /jobs/{job_gid}

Returns the full record for a job.

Parameters

NameDescription
/job_gid string
required
Globally unique identifier for the job.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 JobSuccessfully retrieved Job.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Organization Exports

POST /organization_exports
GET /organization_exports/{organization_export_gid}

An organization_export object represents a request to export the complete data of an Organization in JSON format.

To export an Organization using this API:

  • Create an organization_export request and store the id that is returned.
  • Request the organization_export every few minutes, until the state field contains ‘finished’.
  • Download the file located at the URL in the download_url field. * Exports can take a long time, from several minutes to a few hours for large Organizations.

Note: These endpoints are only available to Service Accounts of an Enterprise Organization.


Create an organization export request

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/organization_exports \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "organization": "1331"
  }
}

201 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "task",
    "created_at": "2012-02-22T02:06:58.147Z",
    "download_url": "https://asana-export.s3.amazonaws.com/export-4632784536274-20170127-43246.json.gz?AWSAccessKeyId=xxxxxxxx",
    "state": "started",
    "organization": {
      "gid": "14916",
      "name": "My Workspace"
    }
  }
}

POST /organization_exports

This method creates a request to export an Organization. Asana will complete the export at some point after you create the request.

Parameters

NameDescription
body object
required
The organization to export.
data objectnone
organization stringGlobally unique identifier for the workspace or organization.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
201 OrganizationExportSuccessfully created organization export request.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get details on an org export request

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/organization_exports/{organization_export_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "task",
    "created_at": "2012-02-22T02:06:58.147Z",
    "download_url": "https://asana-export.s3.amazonaws.com/export-4632784536274-20170127-43246.json.gz?AWSAccessKeyId=xxxxxxxx",
    "state": "started",
    "organization": {
      "gid": "14916",
      "name": "My Workspace"
    }
  }
}

GET /organization_exports/{organization_export_gid}

Returns details of a previously-requested Organization export.

Parameters

NameDescription
/organization_export_gid string
required
Globally unique identifier for the organization export.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 OrganizationExportSuccessfully retrieved organization export object.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Portfolios

GET /portfolios
POST /portfolios
GET /portfolios/{portfolio_gid}
PUT /portfolios/{portfolio_gid}
DELETE /portfolios/{portfolio_gid}
GET /portfolios/{portfolio_gid}/items
POST /portfolios/{portfolio_gid}/addItem
POST /portfolios/{portfolio_gid}/removeItem
POST /portfolios/{portfolio_gid}/addMembers
POST /portfolios/{portfolio_gid}/removeMembers

A 'portfolio' gives a high-level overview of the status of multiple initiatives in Asana. Portfolios provide a dashboard overview of the state of multiple projects, including a progress report and the most recent project status update. Portfolios have some restrictions on size. Each portfolio has a max of 250 items and, like projects, a max of 20 custom fields.


Get multiple portfolios

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/portfolios?workspace=1331&owner=14916 \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "portfolio",
      "name": "Bug Task"
    }
  ]
}

GET /portfolios

Returns a list of the portfolios in compact representation that are owned by the current API user.

Parameters

NameDescription
?workspace string
required
The workspace or organization to filter portfolios on.
?owner string
required
The user who owns the portfolio. Currently, API users can only get a list of portfolios that they themselves own.
?limit integerResults per page.
?offset stringOffset token.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 PortfolioSuccessfully retrieved portfolios.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Create a portfolio

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/portfolios \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Bug Task",
    "color": "light-green",
    "workspace": {
      "name": "My Company Workspace"
    },
    "members": [
      "12345"
    ]
  }
}

201 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "portfolio",
      "name": "Bug Task"
    }
  ]
}

POST /portfolios

Creates a new portfolio in the given workspace with the supplied name.

Note that portfolios created in the Asana UI may have some state (like the “Priority” custom field) which is automatically added to the portfolio when it is created. Portfolios created via our API will not be created with the same initial state to allow integrations to create their own starting state on a portfolio.

Parameters

NameDescription
body object
required
The portfolio to create.
data objectA portfolio gives a high-level overview of the status of multiple initiatives in Asana. Portfolios provide a dashboard overview of the state of multiple projects, including a progress report and the most recent project status update.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
created_at string(date-time)The time at which this resource was created.
created_by object¦nullThe user who created this resource.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
color stringColor of the portfolio.
custom_field_settings [object]Array of custom field settings applied to the portfolio.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
project objectDeprecated: new integrations should prefer the parent field. The id of the project that this custom field settings refers to.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringName of the project. This is generally a short sentence fragment that fits on a line in the UI for maximum readability. However, it can be longer.
is_important booleanis_important is used in the Asana web application to determine if this custom field is displayed in the list/grid view of a project or portfolio.
parent objectThe parent to which the custom field is applied. This can be a project or portfolio and indicates that the tasks or projects that the parent contains may be given custom field values for this custom field.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringName of the project. This is generally a short sentence fragment that fits on a line in the UI for maximum readability. However, it can be longer.
custom_field objectThe custom field that is applied to the parent. readOnly: true
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
resource_subtype stringThe type of the custom field. Must be one of the given values.
type stringDeprecated: new integrations should prefer the resource_subtype field. The type of the custom field. Must be one of the given values.
enum_options [object]Conditional. Only relevant for custom fields of type enum. This array specifies the possible values which an enum custom field can adopt. To modify the enum options, refer to working with enum options.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enum_value objectConditional. Only relevant for custom fields of type enum. This object is the chosen value of an enum custom field.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enabled booleanConditional. Determines if the custom field is enabled or not.
text_value stringConditional. This string is the value of a text custom field.
description stringOpt In. The description of the custom field.
precision integerOnly relevant for custom fields of type ‘Number’. This field dictates the number of places after the decimal to round to, i.e. 0 is integer values, 1 rounds to the nearest tenth, and so on. Must be between 0 and 6, inclusive.
is_global_to_workspace booleanThis flag describes whether this custom field is available to every container in the workspace. Before project-specific custom fields, this field was always true.
has_notifications_enabled booleanThis flag describes whether a follower of a task with this field should receive inbox notifications from changes to this field.
owner objectThe current owner of the portfolio.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
workspace objectCreate-only. The workspace or organization that the portfolio belongs to.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
members [string]Array of object Gids.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Enumerated Values

ParameterValue
colordark-pink
colordark-green
colordark-blue
colordark-red
colordark-teal
colordark-brown
colordark-orange
colordark-purple
colordark-warm-gray
colorlight-pink
colorlight-green
colorlight-blue
colorlight-red
colorlight-teal
colorlight-brown
colorlight-orange
colorlight-purple
colorlight-warm-gray
resource_subtypetext
resource_subtypeenum
resource_subtypenumber
typetext
typeenum
typenumber

Responses

StatusDescription
201 PortfolioSuccessfully retrieved portfolios.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get a portfolio

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/portfolios/{portfolio_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "portfolio",
    "name": "Bug Task",
    "created_at": "2012-02-22T02:06:58.147Z",
    "created_by": {
      "gid": "12345",
      "resource_type": "user",
      "name": "Greg Sanchez"
    },
    "color": "light-green",
    "custom_field_settings": [
      {
        "gid": "12345",
        "resource_type": "custom_field_setting",
        "project": {
          "gid": "12345",
          "resource_type": "project",
          "name": "Stuff to buy"
        },
        "is_important": false,
        "parent": {
          "gid": "12345",
          "resource_type": "project",
          "name": "Stuff to buy"
        },
        "custom_field": {
          "gid": "12345",
          "resource_type": "custom_field",
          "name": "Bug Task",
          "resource_subtype": "text",
          "type": "text",
          "enum_options": [
            {
              "gid": "12345",
              "resource_type": "enum_option",
              "name": "Low",
              "enabled": true,
              "color": "blue"
            }
          ],
          "enum_value": {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          },
          "enabled": true,
          "text_value": "Some Value",
          "description": "Development team priority",
          "precision": 2,
          "is_global_to_workspace": true,
          "has_notifications_enabled": true
        }
      }
    ],
    "owner": {
      "gid": "12345",
      "resource_type": "user",
      "name": "Greg Sanchez"
    },
    "workspace": {
      "gid": "12345",
      "resource_type": "workspace",
      "name": "My Company Workspace"
    },
    "members": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ]
  }
}

GET /portfolios/{portfolio_gid}

Returns the complete portfolio record for a single portfolio.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 PortfolioSuccessfully retrieved the requested portfolio.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Update a portfolio

Code samples

# You can also use wget
curl -X PUT https://app.asana.com/api/1.0/portfolios/{portfolio_gid} \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Bug Task",
    "color": "light-green",
    "workspace": {
      "name": "My Company Workspace"
    },
    "members": [
      "12345"
    ]
  }
}

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "portfolio",
    "name": "Bug Task",
    "created_at": "2012-02-22T02:06:58.147Z",
    "created_by": {
      "gid": "12345",
      "resource_type": "user",
      "name": "Greg Sanchez"
    },
    "color": "light-green",
    "custom_field_settings": [
      {
        "gid": "12345",
        "resource_type": "custom_field_setting",
        "project": {
          "gid": "12345",
          "resource_type": "project",
          "name": "Stuff to buy"
        },
        "is_important": false,
        "parent": {
          "gid": "12345",
          "resource_type": "project",
          "name": "Stuff to buy"
        },
        "custom_field": {
          "gid": "12345",
          "resource_type": "custom_field",
          "name": "Bug Task",
          "resource_subtype": "text",
          "type": "text",
          "enum_options": [
            {
              "gid": "12345",
              "resource_type": "enum_option",
              "name": "Low",
              "enabled": true,
              "color": "blue"
            }
          ],
          "enum_value": {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          },
          "enabled": true,
          "text_value": "Some Value",
          "description": "Development team priority",
          "precision": 2,
          "is_global_to_workspace": true,
          "has_notifications_enabled": true
        }
      }
    ],
    "owner": {
      "gid": "12345",
      "resource_type": "user",
      "name": "Greg Sanchez"
    },
    "workspace": {
      "gid": "12345",
      "resource_type": "workspace",
      "name": "My Company Workspace"
    },
    "members": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ]
  }
}

PUT /portfolios/{portfolio_gid}

An existing portfolio can be updated by making a PUT request on the URL for that portfolio. Only the fields provided in the data block will be updated; any unspecified fields will remain unchanged.

Returns the complete updated portfolio record.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
body object
required
The updated fields for the portfolio.
data objectA portfolio gives a high-level overview of the status of multiple initiatives in Asana. Portfolios provide a dashboard overview of the state of multiple projects, including a progress report and the most recent project status update.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
created_at string(date-time)The time at which this resource was created.
created_by object¦nullThe user who created this resource.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
color stringColor of the portfolio.
custom_field_settings [object]Array of custom field settings applied to the portfolio.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
project objectDeprecated: new integrations should prefer the parent field. The id of the project that this custom field settings refers to.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringName of the project. This is generally a short sentence fragment that fits on a line in the UI for maximum readability. However, it can be longer.
is_important booleanis_important is used in the Asana web application to determine if this custom field is displayed in the list/grid view of a project or portfolio.
parent objectThe parent to which the custom field is applied. This can be a project or portfolio and indicates that the tasks or projects that the parent contains may be given custom field values for this custom field.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringName of the project. This is generally a short sentence fragment that fits on a line in the UI for maximum readability. However, it can be longer.
custom_field objectThe custom field that is applied to the parent. readOnly: true
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
resource_subtype stringThe type of the custom field. Must be one of the given values.
type stringDeprecated: new integrations should prefer the resource_subtype field. The type of the custom field. Must be one of the given values.
enum_options [object]Conditional. Only relevant for custom fields of type enum. This array specifies the possible values which an enum custom field can adopt. To modify the enum options, refer to working with enum options.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enum_value objectConditional. Only relevant for custom fields of type enum. This object is the chosen value of an enum custom field.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enabled booleanConditional. Determines if the custom field is enabled or not.
text_value stringConditional. This string is the value of a text custom field.
description stringOpt In. The description of the custom field.
precision integerOnly relevant for custom fields of type ‘Number’. This field dictates the number of places after the decimal to round to, i.e. 0 is integer values, 1 rounds to the nearest tenth, and so on. Must be between 0 and 6, inclusive.
is_global_to_workspace booleanThis flag describes whether this custom field is available to every container in the workspace. Before project-specific custom fields, this field was always true.
has_notifications_enabled booleanThis flag describes whether a follower of a task with this field should receive inbox notifications from changes to this field.
owner objectThe current owner of the portfolio.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
workspace objectCreate-only. The workspace or organization that the portfolio belongs to.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
members [string]Array of object Gids.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Enumerated Values

ParameterValue
colordark-pink
colordark-green
colordark-blue
colordark-red
colordark-teal
colordark-brown
colordark-orange
colordark-purple
colordark-warm-gray
colorlight-pink
colorlight-green
colorlight-blue
colorlight-red
colorlight-teal
colorlight-brown
colorlight-orange
colorlight-purple
colorlight-warm-gray
resource_subtypetext
resource_subtypeenum
resource_subtypenumber
typetext
typeenum
typenumber

Responses

StatusDescription
200 PortfolioSuccessfully updated the portfolio.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Delete a portfolio

Code samples

# You can also use wget
curl -X DELETE https://app.asana.com/api/1.0/portfolios/{portfolio_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {}
}

DELETE /portfolios/{portfolio_gid}

An existing portfolio can be deleted by making a DELETE request on the URL for that portfolio.

Returns an empty data record.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully deleted the specified portfolio.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get portfolio items

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/portfolios/{portfolio_gid}/items \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "project",
      "name": "Stuff to buy",
      "created_at": "2012-02-22T02:06:58.147Z",
      "archived": false,
      "color": "light-green",
      "current_status": {
        "color": "green",
        "text": "Everything is great",
        "author": {
          "gid": "12345",
          "name": "Greg Bizarro"
        }
      },
      "custom_fields": [
        {
          "gid": "12345",
          "resource_type": "custom_field",
          "name": "Bug Task",
          "resource_subtype": "text",
          "type": "text",
          "enum_options": [
            {
              "gid": "12345",
              "resource_type": "enum_option",
              "name": "Low",
              "enabled": true,
              "color": "blue"
            }
          ],
          "enum_value": {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          },
          "enabled": true,
          "text_value": "Some Value"
        }
      ],
      "custom_field_settings": [
        {
          "gid": "12345",
          "resource_type": "custom_field_setting"
        }
      ],
      "default_view": "calendar",
      "due_date": "2019-09-15",
      "due_on": "2019-09-15",
      "followers": [
        {
          "gid": "12345",
          "resource_type": "user",
          "name": "Greg Sanchez"
        }
      ],
      "html_notes": "These are things we need to purchase.",
      "is_template": false,
      "layout": "list",
      "members": [
        {
          "gid": "12345",
          "resource_type": "user",
          "name": "Greg Sanchez"
        }
      ],
      "modified_at": "2012-02-22T02:06:58.147Z",
      "notes": "These are things we need to purchase.",
      "owner": {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      },
      "public": false,
      "section_migration_status": "not_migrated",
      "start_on": "2019-09-14",
      "team": {
        "gid": "12345",
        "resource_type": "team",
        "name": "Bug Task"
      },
      "workspace": {
        "gid": "12345",
        "resource_type": "workspace",
        "name": "My Company Workspace"
      }
    }
  ]
}

GET /portfolios/{portfolio_gid}/items

Get a list of the items in compact form in a portfolio.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 ProjectSuccessfully retrieved the requested portfolio's items.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Add a portfolio item

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/portfolios/{portfolio_gid}/addItem \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "item": "1331",
  "insert_before": "1331",
  "insert_after": "1331"
}

200 Response

{
  "data": {}
}

POST /portfolios/{portfolio_gid}/addItem

Add an item to a portfolio. Returns an empty data block.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
body object
required
Information about the item being inserted.
item string
required
The item to add to the portfolio.
insert_before stringAn id of an item in this portfolio. The new item will be added before the one specified here. insert_before and insert_after parameters cannot both be specified.
insert_after stringAn id of an item in this portfolio. The new item will be added after the one specified here. insert_before and insert_after parameters cannot both be specified.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully added the item to the portfolio.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Remove a portfolio item

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/portfolios/{portfolio_gid}/removeItem \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "item": "1331"
}

200 Response

{
  "data": {}
}

POST /portfolios/{portfolio_gid}/removeItem

Remove an item from a portfolio. Returns an empty data block.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
body object
required
Information about the item being removed.
item string
required
The item to remove from the portfolio.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully removed the item from the portfolio.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Add users to a portfolio

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/portfolios/{portfolio_gid}/addMembers \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "members": "521621,621373"
}

200 Response

{
  "data": {}
}

POST /portfolios/{portfolio_gid}/addMembers

Adds the specified list of users as members of the portfolio. Returns the updated portfolio record.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
body object
required
Information about the members being added.
members string
required
An array of user ids.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully added members to the portfolio.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Remove users from a portfolio

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/portfolios/{portfolio_gid}/removeMembers \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "members": "521621,621373"
}

200 Response

{
  "data": {}
}

POST /portfolios/{portfolio_gid}/removeMembers

Removes the specified list of users from members of the portfolio. Returns the updated portfolio record.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
body object
required
Information about the members being removed.
members string
required
An array of user ids.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully removed the members from the portfolio.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Portfolio Memberships

GET /portfolio_memberships
GET /portfolio_memberships/{portfolio_membership_gid}
GET /portfolios/{portfolio_gid}/portfolio_memberships

This object determines if a user is a member of a portfolio.


Get multiple portfolio memberships

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/portfolio_memberships \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "portfolio",
      "name": "Bug Task"
    }
  ]
}

GET /portfolio_memberships

Returns a list of portfolio memberships in compact representation. You must specify portfolio, portfolio and user, or workspace and user.

Parameters

NameDescription
?portfolio stringThe portfolio to filter results on.
?workspace stringThe workspace to filter results on.
?user string(email)The user to filter results on.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 PortfolioSuccessfully retrieved portfolio memberships.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get a portfolio membership

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/portfolio_memberships/{portfolio_membership_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "portfolio_membership",
    "user": {
      "gid": "12345",
      "resource_type": "user",
      "name": "Greg Sanchez"
    },
    "portfolio": {
      "gid": "12345",
      "resource_type": "portfolio",
      "name": "Bug Task"
    }
  }
}

GET /portfolio_memberships/{portfolio_membership_gid}

Returns the complete portfolio record for a single portfolio membership.

Parameters

NameDescription
/portfolio_membership_path_gid string
required
none
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 PortfolioMembershipSuccessfully retrieved the requested portfolio membership.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get memberships from a portfolio

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/portfolios/{portfolio_gid}/portfolio_memberships \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "portfolio_membership",
      "user": {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    }
  ]
}

GET /portfolios/{portfolio_gid}/portfolio_memberships

Returns the compact portfolio membership records for the portfolio.

Parameters

NameDescription
/portfolio_gid string
required
Globally unique identifier for the portfolio.
?user string(email)The user to filter results on.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 PortfolioMembershipSuccessfully retrieved the requested portfolio's memberships.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Projects

GET /projects
POST /projects
GET /projects/{project_gid}
PUT /projects/{project_gid}
DELETE /projects/{project_gid}
POST /projects/{project_gid}/duplicate
GET /tasks/{task_gid}/projects
GET /teams/{team_gid}/projects
POST /teams/{team_gid}/projects
GET /workspaces/{workspace_gid}/projects
POST /workspaces/{workspace_gid}/projects
POST /projects/{project_gid}/addCustomFieldSetting
POST /projects/{project_gid}/removeCustomFieldSetting
GET /projects/{project_gid}/task_counts

A project represents a prioritized list of tasks in Asana or a board with columns of tasks represented as cards. It exists in a single workspace or organization and is accessible to a subset of users in that workspace or organization, depending on its permissions.

Projects in organizations are shared with a single team. You cannot currently change the team of a project via the API. Non-organization workspaces do not have teams and so you should not specify the team of project in a regular workspace.

Followers of a project are a subset of the members of that project. Followers of a project will receive all updates including tasks created, added and removed from that project. Members of the project have access to and will receive status updates of the project. Adding followers to a project will add them as members if they are not already, removing followers from a project will not affect membership.


Get multiple projects

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/projects \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "project",
      "name": "Stuff to buy",
      "created_at": "2012-02-22T02:06:58.147Z",
      "archived": false,
      "color": "light-green",
      "current_status": {
        "color": "green",
        "text": "Everything is great",
        "author": {
          "gid": "12345",
          "name": "Greg Bizarro"
        }
      },
      "custom_fields": [
        {
          "gid": "12345",
          "resource_type": "custom_field",
          "name": "Bug Task",
          "resource_subtype": "text",
          "type": "text",
          "enum_options": [
            {
              "gid": "12345",
              "resource_type": "enum_option",
              "name": "Low",
              "enabled": true,
              "color": "blue"
            }
          ],
          "enum_value": {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          },
          "enabled": true,
          "text_value": "Some Value"
        }
      ],
      "custom_field_settings": [
        {
          "gid": "12345",
          "resource_type": "custom_field_setting"
        }
      ],
      "default_view": "calendar",
      "due_date": "2019-09-15",
      "due_on": "2019-09-15",
      "followers": [
        {
          "gid": "12345",
          "resource_type": "user",
          "name": "Greg Sanchez"
        }
      ],
      "html_notes": "These are things we need to purchase.",
      "is_template": false,
      "layout": "list",
      "members": [
        {
          "gid": "12345",
          "resource_type": "user",
          "name": "Greg Sanchez"
        }
      ],
      "modified_at": "2012-02-22T02:06:58.147Z",
      "notes": "These are things we need to purchase.",
      "owner": {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      },
      "public": false,
      "section_migration_status": "not_migrated",
      "start_on": "2019-09-14",
      "team": {
        "gid": "12345",
        "resource_type": "team",
        "name": "Bug Task"
      },
      "workspace": {
        "gid": "12345",
        "resource_type": "workspace",
        "name": "My Company Workspace"
      }
    }
  ]
}

GET /projects

Returns the compact project records for some filtered set of projects. Use one or more of the parameters provided to filter the projects returned.

Parameters

NameDescription
?workspace stringThe workspace or organization to filter projects on.
?team stringThe team to filter projects on.
?archived booleanOnly return projects whose archived field takes on the value of this parameter.
?limit integerResults per page.
?offset stringOffset token.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 ProjectSuccessfully retrieved projects.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Create a project

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/projects \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Bug Project",
    "notes": "For tracking pesky bugs.",
    "workspace": "1331",
    "team": "14916"
  }
}

201 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "project",
    "name": "Stuff to buy",
    "created_at": "2012-02-22T02:06:58.147Z",
    "archived": false,
    "color": "light-green",
    "current_status": {
      "color": "green",
      "text": "Everything is great",
      "author": {
        "gid": "12345",
        "name": "Greg Bizarro"
      }
    },
    "custom_fields": [
      {
        "gid": "12345",
        "resource_type": "custom_field",
        "name": "Bug Task",
        "resource_subtype": "text",
        "type": "text",
        "enum_options": [
          {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          }
        ],
        "enum_value": {
          "gid": "12345",
          "resource_type": "enum_option",
          "name": "Low",
          "enabled": true,
          "color": "blue"
        },
        "enabled": true,
        "text_value": "Some Value"
      }
    ],
    "custom_field_settings": [
      {
        "gid": "12345",
        "resource_type": "custom_field_setting"
      }
    ],
    "default_view": "calendar",
    "due_date": "2019-09-15",
    "due_on": "2019-09-15",
    "followers": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ],
    "html_notes": "These are things we need to purchase.",
    "is_template": false,
    "layout": "list",
    "members": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ],
    "modified_at": "2012-02-22T02:06:58.147Z",
    "notes": "These are things we need to purchase.",
    "owner": {
      "gid": "12345",
      "resource_type": "user",
      "name": "Greg Sanchez"
    },
    "public": false,
    "section_migration_status": "not_migrated",
    "start_on": "2019-09-14",
    "team": {
      "gid": "12345",
      "resource_type": "team",
      "name": "Bug Task"
    },
    "workspace": {
      "gid": "12345",
      "resource_type": "workspace",
      "name": "My Company Workspace"
    }
  }
}

POST /projects

Create a new project in a workspace or team.

Every project is required to be created in a specific workspace or organization, and this cannot be changed once set. Note that you can use the workspace parameter regardless of whether or not it is an organization.

If the workspace for your project is an organization, you must also supply a team to share the project with.

Returns the full record of the newly created project.

Parameters

NameDescription
body object
required
The project to create.
data objectnone
name stringThe name of the project.
notes stringThe description of the project.
workspace stringThe workspace or organization to create the project in.
team stringIf creating in an organization, the specific team to create the project in.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
201 ProjectSuccessfully retrieved projects.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get a project

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/projects/{project_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "project",
    "name": "Stuff to buy",
    "created_at": "2012-02-22T02:06:58.147Z",
    "archived": false,
    "color": "light-green",
    "current_status": {
      "color": "green",
      "text": "Everything is great",
      "author": {
        "gid": "12345",
        "name": "Greg Bizarro"
      }
    },
    "custom_fields": [
      {
        "gid": "12345",
        "resource_type": "custom_field",
        "name": "Bug Task",
        "resource_subtype": "text",
        "type": "text",
        "enum_options": [
          {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          }
        ],
        "enum_value": {
          "gid": "12345",
          "resource_type": "enum_option",
          "name": "Low",
          "enabled": true,
          "color": "blue"
        },
        "enabled": true,
        "text_value": "Some Value"
      }
    ],
    "custom_field_settings": [
      {
        "gid": "12345",
        "resource_type": "custom_field_setting"
      }
    ],
    "default_view": "calendar",
    "due_date": "2019-09-15",
    "due_on": "2019-09-15",
    "followers": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ],
    "html_notes": "These are things we need to purchase.",
    "is_template": false,
    "layout": "list",
    "members": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ],
    "modified_at": "2012-02-22T02:06:58.147Z",
    "notes": "These are things we need to purchase.",
    "owner": {
      "gid": "12345",
      "resource_type": "user",
      "name": "Greg Sanchez"
    },
    "public": false,
    "section_migration_status": "not_migrated",
    "start_on": "2019-09-14",
    "team": {
      "gid": "12345",
      "resource_type": "team",
      "name": "Bug Task"
    },
    "workspace": {
      "gid": "12345",
      "resource_type": "workspace",
      "name": "My Company Workspace"
    }
  }
}

GET /projects/{project_gid}

Returns the complete project record for a single project.

Parameters

NameDescription
/project_gid string
required
Globally unique identifier for the project.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 ProjectSuccessfully retrieved the requested project.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Update a project

Code samples

# You can also use wget
curl -X PUT https://app.asana.com/api/1.0/projects/{project_gid} \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Stuff to buy",
    "archived": false,
    "color": "light-green",
    "default_view": "calendar",
    "due_date": "2019-09-15",
    "due_on": "2019-09-15",
    "html_notes": "These are things we need to purchase.",
    "is_template": false,
    "notes": "These are things we need to purchase.",
    "owner": {
      "name": "Greg Sanchez"
    },
    "public": false,
    "start_on": "2019-09-14",
    "team": {
      "name": "Bug Task"
    }
  }
}

200 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "project",
    "name": "Stuff to buy",
    "created_at": "2012-02-22T02:06:58.147Z",
    "archived": false,
    "color": "light-green",
    "current_status": {
      "color": "green",
      "text": "Everything is great",
      "author": {
        "gid": "12345",
        "name": "Greg Bizarro"
      }
    },
    "custom_fields": [
      {
        "gid": "12345",
        "resource_type": "custom_field",
        "name": "Bug Task",
        "resource_subtype": "text",
        "type": "text",
        "enum_options": [
          {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          }
        ],
        "enum_value": {
          "gid": "12345",
          "resource_type": "enum_option",
          "name": "Low",
          "enabled": true,
          "color": "blue"
        },
        "enabled": true,
        "text_value": "Some Value"
      }
    ],
    "custom_field_settings": [
      {
        "gid": "12345",
        "resource_type": "custom_field_setting"
      }
    ],
    "default_view": "calendar",
    "due_date": "2019-09-15",
    "due_on": "2019-09-15",
    "followers": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ],
    "html_notes": "These are things we need to purchase.",
    "is_template": false,
    "layout": "list",
    "members": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ],
    "modified_at": "2012-02-22T02:06:58.147Z",
    "notes": "These are things we need to purchase.",
    "owner": {
      "gid": "12345",
      "resource_type": "user",
      "name": "Greg Sanchez"
    },
    "public": false,
    "section_migration_status": "not_migrated",
    "start_on": "2019-09-14",
    "team": {
      "gid": "12345",
      "resource_type": "team",
      "name": "Bug Task"
    },
    "workspace": {
      "gid": "12345",
      "resource_type": "workspace",
      "name": "My Company Workspace"
    }
  }
}

PUT /projects/{project_gid}

A specific, existing project can be updated by making a PUT request on the URL for that project. Only the fields provided in the data block will be updated; any unspecified fields will remain unchanged.

When using this method, it is best to specify only those fields you wish to change, or else you may overwrite changes made by another user since you last retrieved the task.

Returns the complete updated project record.

Parameters

NameDescription
/project_gid string
required
Globally unique identifier for the project.
body object
required
The updated fields for the project.
data objectA project represents a prioritized list of tasks in Asana or a board with columns of tasks represented as cards. It exists in a single workspace or organization and is accessible to a subset of users in that workspace or organization, depending on its permissions.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringName of the project. This is generally a short sentence fragment that fits on a line in the UI for maximum readability. However, it can be longer.
created_at string(date-time)The time at which this resource was created.
archived booleanTrue if the project is archived, false if not. Archived projects do not show in the UI by default and may be treated differently for queries.
color string¦nullColor of the project.
current_status object¦nullThe most recently created status update for the project, or null if no update exists. See also the documentation for project status updates.
color stringnone
text stringnone
author objectA user object represents an account in Asana that can be given access to various workspaces, projects, and tasks.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
custom_fields [object]Array of Custom Fields.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
resource_subtype stringThe type of the custom field. Must be one of the given values.
type stringDeprecated: new integrations should prefer the resource_subtype field. The type of the custom field. Must be one of the given values.
enum_options [object]Conditional. Only relevant for custom fields of type enum. This array specifies the possible values which an enum custom field can adopt. To modify the enum options, refer to working with enum options.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enum_value objectConditional. Only relevant for custom fields of type enum. This object is the chosen value of an enum custom field.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enabled booleanConditional. Determines if the custom field is enabled or not.
text_value stringConditional. This string is the value of a text custom field.
custom_field_settings [object]Array of Custom Field Settings (in compact form).
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
default_view stringThe default view (list, board, calendar, or timeline) of a project.
due_date string(date-time)¦nullDeprecated: new integrations should prefer the due_on field.
due_on string(date-time)¦nullThe day on which this project is due. This takes a date with format YYYY-MM-DD.
followers [object]Array of users following this project. Followers are a subset of members who receive all notifications for a project, the default notification setting when adding members to a project in-product.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
html_notes stringOpt In. The notes of the project with formatting as HTML.
is_template booleanOpt In. Determines if the project is a template.
layout stringThe layout (board or list view) of a project
members [object]Array of users who are members of this project.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
modified_at string(date-time)The time at which this project was last modified.
notes stringMore detailed, free-form textual information associated with the project.
owner object¦nullThe current owner of the project, may be null.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
public booleanTrue if the project is public to the organization. If false, do not share this project with other users in this organization without explicitly checking to see if they have access.
section_migration_status stringRead-only The section migration status of this project.
start_on string(date)¦nullThe day on which work for this project begins, or null if the project has no start date. This takes a date with YYYY-MM-DD format. Note: due_on or due_at must be present in the request when setting or unsetting the start_on parameter.
team objectCreate-only. The team that this project is shared with. This field only exists for projects in organizations.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
workspace objectCreate-only. The workspace or organization this project is associated with. Once created, projects cannot be moved to a different workspace. This attribute can only be specified at creation time.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Detailed descriptions

html_notes: Opt In. The notes of the project with formatting as HTML. Note: This field is under active migration—please see our blog post for more information.

modified_at: The time at which this project was last modified. Note: This does not currently reflect any changes in associations such as tasks or comments that may have been added or removed from the project.

Enumerated Values

ParameterValue
colordark-pink
colordark-green
colordark-blue
colordark-red
colordark-teal
colordark-brown
colordark-orange
colordark-purple
colordark-warm-gray
colorlight-pink
colorlight-green
colorlight-blue
colorlight-red
colorlight-teal
colorlight-brown
colorlight-orange
colorlight-purple
colorlight-warm-gray
colorgreen
coloryellow
colorred
resource_subtypetext
resource_subtypeenum
resource_subtypenumber
typetext
typeenum
typenumber
default_viewlist
default_viewboard
default_viewcalendar
default_viewtimeline
layoutlist
layoutboard
section_migration_statusnot_migrated
section_migration_statusin_progress
section_migration_statuscompleted

Responses

StatusDescription
200 ProjectSuccessfully updated the project.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Delete a project

Code samples

# You can also use wget
curl -X DELETE https://app.asana.com/api/1.0/projects/{project_gid} \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {}
}

DELETE /projects/{project_gid}

A specific, existing project can be deleted by making a DELETE request on the URL for that project.

Returns an empty data record.

Parameters

NameDescription
/project_gid string
required
Globally unique identifier for the project.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully deleted the specified project.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Duplicate a project

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/projects/{project_gid}/duplicate \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "New Project Name",
    "team": "12345",
    "include": [
      "members",
      "task_notes"
    ],
    "schedule_dates": {
      "should_skip_weekends": true,
      "due_on": "2019-05-21",
      "start_on": "2019-05-21"
    }
  }
}

201 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "task",
    "resource_subtype": "duplicate_task",
    "status": "in_progress",
    "new_project": {
      "gid": "12345",
      "resource_type": "project",
      "name": "Stuff to buy"
    },
    "new_task": {
      "gid": "12345",
      "resource_type": "task",
      "name": "Bug Task"
    }
  }
}

POST /projects/{project_gid}/duplicate

Creates and returns a job that will asynchronously handle the duplication.

Parameters

NameDescription
/project_gid string
required
Globally unique identifier for the project.
body objectDescribes the duplicate's name and the elements that will be duplicated.
data objectnone
name string
required
The name of the new project.
team stringSets the team of the new project. If team is not defined, the new project will be in the same team as the the original project.
include stringThe elements that will be duplicated to the new project. Tasks are always included.
schedule_dates objectA dictionary of options to auto-shift dates. task_dates must be included to use this option. Requires either start_on or due_on, but not both.
should_skip_weekends boolean
required
Determines if the auto-shifted dates should skip weekends.
due_on stringSets the last due date in the duplicated project to the given date. The rest of the due dates will be offset by the same amount as the due dates in the original project.
start_on stringSets the first start date in the duplicated project to the given date. The rest of the start dates will be offset by the same amount as the start dates in the original project.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Enumerated Values

ParameterValue
includemembers
includenotes
includetask_notes
includetask_assignee
includetask_subtasks
includetask_attachments
includetask_dates
includetask_dependencies
includetask_followers
includetask_tags
includetask_projects

Responses

StatusDescription
201 JobSuccessfully created the job to handle duplication.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get projects a task is in

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/tasks/{task_gid}/projects \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "project",
      "name": "Stuff to buy",
      "created_at": "2012-02-22T02:06:58.147Z",
      "archived": false,
      "color": "light-green",
      "current_status": {
        "color": "green",
        "text": "Everything is great",
        "author": {
          "gid": "12345",
          "name": "Greg Bizarro"
        }
      },
      "custom_fields": [
        {
          "gid": "12345",
          "resource_type": "custom_field",
          "name": "Bug Task",
          "resource_subtype": "text",
          "type": "text",
          "enum_options": [
            {
              "gid": "12345",
              "resource_type": "enum_option",
              "name": "Low",
              "enabled": true,
              "color": "blue"
            }
          ],
          "enum_value": {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          },
          "enabled": true,
          "text_value": "Some Value"
        }
      ],
      "custom_field_settings": [
        {
          "gid": "12345",
          "resource_type": "custom_field_setting"
        }
      ],
      "default_view": "calendar",
      "due_date": "2019-09-15",
      "due_on": "2019-09-15",
      "followers": [
        {
          "gid": "12345",
          "resource_type": "user",
          "name": "Greg Sanchez"
        }
      ],
      "html_notes": "These are things we need to purchase.",
      "is_template": false,
      "layout": "list",
      "members": [
        {
          "gid": "12345",
          "resource_type": "user",
          "name": "Greg Sanchez"
        }
      ],
      "modified_at": "2012-02-22T02:06:58.147Z",
      "notes": "These are things we need to purchase.",
      "owner": {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      },
      "public": false,
      "section_migration_status": "not_migrated",
      "start_on": "2019-09-14",
      "team": {
        "gid": "12345",
        "resource_type": "team",
        "name": "Bug Task"
      },
      "workspace": {
        "gid": "12345",
        "resource_type": "workspace",
        "name": "My Company Workspace"
      }
    }
  ]
}

GET /tasks/{task_gid}/projects

Returns a compact representation of all of the projects the task is in.

Parameters

NameDescription
/task_gid string
required
The task to operate on.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 ProjectSuccessfully retrieved the projects for the given task.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get a team's projects

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/teams/{team_gid}/projects \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "project",
      "name": "Stuff to buy",
      "created_at": "2012-02-22T02:06:58.147Z",
      "archived": false,
      "color": "light-green",
      "current_status": {
        "color": "green",
        "text": "Everything is great",
        "author": {
          "gid": "12345",
          "name": "Greg Bizarro"
        }
      },
      "custom_fields": [
        {
          "gid": "12345",
          "resource_type": "custom_field",
          "name": "Bug Task",
          "resource_subtype": "text",
          "type": "text",
          "enum_options": [
            {
              "gid": "12345",
              "resource_type": "enum_option",
              "name": "Low",
              "enabled": true,
              "color": "blue"
            }
          ],
          "enum_value": {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          },
          "enabled": true,
          "text_value": "Some Value"
        }
      ],
      "custom_field_settings": [
        {
          "gid": "12345",
          "resource_type": "custom_field_setting"
        }
      ],
      "default_view": "calendar",
      "due_date": "2019-09-15",
      "due_on": "2019-09-15",
      "followers": [
        {
          "gid": "12345",
          "resource_type": "user",
          "name": "Greg Sanchez"
        }
      ],
      "html_notes": "These are things we need to purchase.",
      "is_template": false,
      "layout": "list",
      "members": [
        {
          "gid": "12345",
          "resource_type": "user",
          "name": "Greg Sanchez"
        }
      ],
      "modified_at": "2012-02-22T02:06:58.147Z",
      "notes": "These are things we need to purchase.",
      "owner": {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      },
      "public": false,
      "section_migration_status": "not_migrated",
      "start_on": "2019-09-14",
      "team": {
        "gid": "12345",
        "resource_type": "team",
        "name": "Bug Task"
      },
      "workspace": {
        "gid": "12345",
        "resource_type": "workspace",
        "name": "My Company Workspace"
      }
    }
  ]
}

GET /teams/{team_gid}/projects

Returns the compact project records for all projects in the team.

Parameters

NameDescription
/team_gid string
required
Globally unique identifier for the team.
?archived booleanOnly return projects whose archived field takes on the value of this parameter.
?limit integerResults per page.
?offset stringOffset token.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 ProjectSuccessfully retrieved the requested team's projects.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Create a project in a team

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/teams/{team_gid}/projects \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Stuff to buy",
    "archived": false,
    "color": "light-green",
    "default_view": "calendar",
    "due_date": "2019-09-15",
    "due_on": "2019-09-15",
    "html_notes": "These are things we need to purchase.",
    "is_template": false,
    "notes": "These are things we need to purchase.",
    "owner": {
      "name": "Greg Sanchez"
    },
    "public": false,
    "start_on": "2019-09-14",
    "team": {
      "name": "Bug Task"
    }
  }
}

201 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "project",
    "name": "Stuff to buy",
    "created_at": "2012-02-22T02:06:58.147Z",
    "archived": false,
    "color": "light-green",
    "current_status": {
      "color": "green",
      "text": "Everything is great",
      "author": {
        "gid": "12345",
        "name": "Greg Bizarro"
      }
    },
    "custom_fields": [
      {
        "gid": "12345",
        "resource_type": "custom_field",
        "name": "Bug Task",
        "resource_subtype": "text",
        "type": "text",
        "enum_options": [
          {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          }
        ],
        "enum_value": {
          "gid": "12345",
          "resource_type": "enum_option",
          "name": "Low",
          "enabled": true,
          "color": "blue"
        },
        "enabled": true,
        "text_value": "Some Value"
      }
    ],
    "custom_field_settings": [
      {
        "gid": "12345",
        "resource_type": "custom_field_setting"
      }
    ],
    "default_view": "calendar",
    "due_date": "2019-09-15",
    "due_on": "2019-09-15",
    "followers": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ],
    "html_notes": "These are things we need to purchase.",
    "is_template": false,
    "layout": "list",
    "members": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ],
    "modified_at": "2012-02-22T02:06:58.147Z",
    "notes": "These are things we need to purchase.",
    "owner": {
      "gid": "12345",
      "resource_type": "user",
      "name": "Greg Sanchez"
    },
    "public": false,
    "section_migration_status": "not_migrated",
    "start_on": "2019-09-14",
    "team": {
      "gid": "12345",
      "resource_type": "team",
      "name": "Bug Task"
    },
    "workspace": {
      "gid": "12345",
      "resource_type": "workspace",
      "name": "My Company Workspace"
    }
  }
}

POST /teams/{team_gid}/projects

Creates a project shared with the given team.

Returns the full record of the newly created project.

Parameters

NameDescription
/team_gid string
required
Globally unique identifier for the team.
body object
required
The new project to create.
data objectA project represents a prioritized list of tasks in Asana or a board with columns of tasks represented as cards. It exists in a single workspace or organization and is accessible to a subset of users in that workspace or organization, depending on its permissions.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringName of the project. This is generally a short sentence fragment that fits on a line in the UI for maximum readability. However, it can be longer.
created_at string(date-time)The time at which this resource was created.
archived booleanTrue if the project is archived, false if not. Archived projects do not show in the UI by default and may be treated differently for queries.
color string¦nullColor of the project.
current_status object¦nullThe most recently created status update for the project, or null if no update exists. See also the documentation for project status updates.
color stringnone
text stringnone
author objectA user object represents an account in Asana that can be given access to various workspaces, projects, and tasks.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
custom_fields [object]Array of Custom Fields.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
resource_subtype stringThe type of the custom field. Must be one of the given values.
type stringDeprecated: new integrations should prefer the resource_subtype field. The type of the custom field. Must be one of the given values.
enum_options [object]Conditional. Only relevant for custom fields of type enum. This array specifies the possible values which an enum custom field can adopt. To modify the enum options, refer to working with enum options.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enum_value objectConditional. Only relevant for custom fields of type enum. This object is the chosen value of an enum custom field.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enabled booleanConditional. Determines if the custom field is enabled or not.
text_value stringConditional. This string is the value of a text custom field.
custom_field_settings [object]Array of Custom Field Settings (in compact form).
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
default_view stringThe default view (list, board, calendar, or timeline) of a project.
due_date string(date-time)¦nullDeprecated: new integrations should prefer the due_on field.
due_on string(date-time)¦nullThe day on which this project is due. This takes a date with format YYYY-MM-DD.
followers [object]Array of users following this project. Followers are a subset of members who receive all notifications for a project, the default notification setting when adding members to a project in-product.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
html_notes stringOpt In. The notes of the project with formatting as HTML.
is_template booleanOpt In. Determines if the project is a template.
layout stringThe layout (board or list view) of a project
members [object]Array of users who are members of this project.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
modified_at string(date-time)The time at which this project was last modified.
notes stringMore detailed, free-form textual information associated with the project.
owner object¦nullThe current owner of the project, may be null.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
public booleanTrue if the project is public to the organization. If false, do not share this project with other users in this organization without explicitly checking to see if they have access.
section_migration_status stringRead-only The section migration status of this project.
start_on string(date)¦nullThe day on which work for this project begins, or null if the project has no start date. This takes a date with YYYY-MM-DD format. Note: due_on or due_at must be present in the request when setting or unsetting the start_on parameter.
team objectCreate-only. The team that this project is shared with. This field only exists for projects in organizations.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
workspace objectCreate-only. The workspace or organization this project is associated with. Once created, projects cannot be moved to a different workspace. This attribute can only be specified at creation time.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Detailed descriptions

html_notes: Opt In. The notes of the project with formatting as HTML. Note: This field is under active migration—please see our blog post for more information.

modified_at: The time at which this project was last modified. Note: This does not currently reflect any changes in associations such as tasks or comments that may have been added or removed from the project.

Enumerated Values

ParameterValue
colordark-pink
colordark-green
colordark-blue
colordark-red
colordark-teal
colordark-brown
colordark-orange
colordark-purple
colordark-warm-gray
colorlight-pink
colorlight-green
colorlight-blue
colorlight-red
colorlight-teal
colorlight-brown
colorlight-orange
colorlight-purple
colorlight-warm-gray
colorgreen
coloryellow
colorred
resource_subtypetext
resource_subtypeenum
resource_subtypenumber
typetext
typeenum
typenumber
default_viewlist
default_viewboard
default_viewcalendar
default_viewtimeline
layoutlist
layoutboard
section_migration_statusnot_migrated
section_migration_statusin_progress
section_migration_statuscompleted

Responses

StatusDescription
201 ProjectSuccessfully created the specified project.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get all projects in a workspace

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/workspaces/{workspace_gid}/projects \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": [
    {
      "gid": "12345",
      "resource_type": "project",
      "name": "Stuff to buy",
      "created_at": "2012-02-22T02:06:58.147Z",
      "archived": false,
      "color": "light-green",
      "current_status": {
        "color": "green",
        "text": "Everything is great",
        "author": {
          "gid": "12345",
          "name": "Greg Bizarro"
        }
      },
      "custom_fields": [
        {
          "gid": "12345",
          "resource_type": "custom_field",
          "name": "Bug Task",
          "resource_subtype": "text",
          "type": "text",
          "enum_options": [
            {
              "gid": "12345",
              "resource_type": "enum_option",
              "name": "Low",
              "enabled": true,
              "color": "blue"
            }
          ],
          "enum_value": {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          },
          "enabled": true,
          "text_value": "Some Value"
        }
      ],
      "custom_field_settings": [
        {
          "gid": "12345",
          "resource_type": "custom_field_setting"
        }
      ],
      "default_view": "calendar",
      "due_date": "2019-09-15",
      "due_on": "2019-09-15",
      "followers": [
        {
          "gid": "12345",
          "resource_type": "user",
          "name": "Greg Sanchez"
        }
      ],
      "html_notes": "These are things we need to purchase.",
      "is_template": false,
      "layout": "list",
      "members": [
        {
          "gid": "12345",
          "resource_type": "user",
          "name": "Greg Sanchez"
        }
      ],
      "modified_at": "2012-02-22T02:06:58.147Z",
      "notes": "These are things we need to purchase.",
      "owner": {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      },
      "public": false,
      "section_migration_status": "not_migrated",
      "start_on": "2019-09-14",
      "team": {
        "gid": "12345",
        "resource_type": "team",
        "name": "Bug Task"
      },
      "workspace": {
        "gid": "12345",
        "resource_type": "workspace",
        "name": "My Company Workspace"
      }
    }
  ]
}

GET /workspaces/{workspace_gid}/projects

Returns the compact project records for all projects in the workspace.

Parameters

NameDescription
/workspace_gid string
required
Globally unique identifier for the workspace or organization.
?archived booleanOnly return projects whose archived field takes on the value of this parameter.
?limit integerResults per page.
?offset stringOffset token.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 ProjectSuccessfully retrieved the requested workspace's projects.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Create a project in a workspace

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/workspaces/{workspace_gid}/projects \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

Body parameter

{
  "data": {
    "name": "Stuff to buy",
    "archived": false,
    "color": "light-green",
    "default_view": "calendar",
    "due_date": "2019-09-15",
    "due_on": "2019-09-15",
    "html_notes": "These are things we need to purchase.",
    "is_template": false,
    "notes": "These are things we need to purchase.",
    "owner": {
      "name": "Greg Sanchez"
    },
    "public": false,
    "start_on": "2019-09-14",
    "team": {
      "name": "Bug Task"
    }
  }
}

201 Response

{
  "data": {
    "gid": "12345",
    "resource_type": "project",
    "name": "Stuff to buy",
    "created_at": "2012-02-22T02:06:58.147Z",
    "archived": false,
    "color": "light-green",
    "current_status": {
      "color": "green",
      "text": "Everything is great",
      "author": {
        "gid": "12345",
        "name": "Greg Bizarro"
      }
    },
    "custom_fields": [
      {
        "gid": "12345",
        "resource_type": "custom_field",
        "name": "Bug Task",
        "resource_subtype": "text",
        "type": "text",
        "enum_options": [
          {
            "gid": "12345",
            "resource_type": "enum_option",
            "name": "Low",
            "enabled": true,
            "color": "blue"
          }
        ],
        "enum_value": {
          "gid": "12345",
          "resource_type": "enum_option",
          "name": "Low",
          "enabled": true,
          "color": "blue"
        },
        "enabled": true,
        "text_value": "Some Value"
      }
    ],
    "custom_field_settings": [
      {
        "gid": "12345",
        "resource_type": "custom_field_setting"
      }
    ],
    "default_view": "calendar",
    "due_date": "2019-09-15",
    "due_on": "2019-09-15",
    "followers": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ],
    "html_notes": "These are things we need to purchase.",
    "is_template": false,
    "layout": "list",
    "members": [
      {
        "gid": "12345",
        "resource_type": "user",
        "name": "Greg Sanchez"
      }
    ],
    "modified_at": "2012-02-22T02:06:58.147Z",
    "notes": "These are things we need to purchase.",
    "owner": {
      "gid": "12345",
      "resource_type": "user",
      "name": "Greg Sanchez"
    },
    "public": false,
    "section_migration_status": "not_migrated",
    "start_on": "2019-09-14",
    "team": {
      "gid": "12345",
      "resource_type": "team",
      "name": "Bug Task"
    },
    "workspace": {
      "gid": "12345",
      "resource_type": "workspace",
      "name": "My Company Workspace"
    }
  }
}

POST /workspaces/{workspace_gid}/projects

Returns the compact project records for all projects in the workspace.

If the workspace for your project is an organization, you must also supply a team to share the project with.

Returns the full record of the newly created project.

Parameters

NameDescription
/workspace_gid string
required
Globally unique identifier for the workspace or organization.
body object
required
The new project to create.
data objectA project represents a prioritized list of tasks in Asana or a board with columns of tasks represented as cards. It exists in a single workspace or organization and is accessible to a subset of users in that workspace or organization, depending on its permissions.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringName of the project. This is generally a short sentence fragment that fits on a line in the UI for maximum readability. However, it can be longer.
created_at string(date-time)The time at which this resource was created.
archived booleanTrue if the project is archived, false if not. Archived projects do not show in the UI by default and may be treated differently for queries.
color string¦nullColor of the project.
current_status object¦nullThe most recently created status update for the project, or null if no update exists. See also the documentation for project status updates.
color stringnone
text stringnone
author objectA user object represents an account in Asana that can be given access to various workspaces, projects, and tasks.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
custom_fields [object]Array of Custom Fields.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
resource_subtype stringThe type of the custom field. Must be one of the given values.
type stringDeprecated: new integrations should prefer the resource_subtype field. The type of the custom field. Must be one of the given values.
enum_options [object]Conditional. Only relevant for custom fields of type enum. This array specifies the possible values which an enum custom field can adopt. To modify the enum options, refer to working with enum options.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enum_value objectConditional. Only relevant for custom fields of type enum. This object is the chosen value of an enum custom field.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the enum option.
enabled booleanThe color of the enum option. Defaults to ‘none’.
color stringWhether or not the enum option is a selectable value for the custom field.
enabled booleanConditional. Determines if the custom field is enabled or not.
text_value stringConditional. This string is the value of a text custom field.
custom_field_settings [object]Array of Custom Field Settings (in compact form).
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
default_view stringThe default view (list, board, calendar, or timeline) of a project.
due_date string(date-time)¦nullDeprecated: new integrations should prefer the due_on field.
due_on string(date-time)¦nullThe day on which this project is due. This takes a date with format YYYY-MM-DD.
followers [object]Array of users following this project. Followers are a subset of members who receive all notifications for a project, the default notification setting when adding members to a project in-product.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
html_notes stringOpt In. The notes of the project with formatting as HTML.
is_template booleanOpt In. Determines if the project is a template.
layout stringThe layout (board or list view) of a project
members [object]Array of users who are members of this project.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
modified_at string(date-time)The time at which this project was last modified.
notes stringMore detailed, free-form textual information associated with the project.
owner object¦nullThe current owner of the project, may be null.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringRead-only except when same user as requester. The user’s name.
public booleanTrue if the project is public to the organization. If false, do not share this project with other users in this organization without explicitly checking to see if they have access.
section_migration_status stringRead-only The section migration status of this project.
start_on string(date)¦nullThe day on which work for this project begins, or null if the project has no start date. This takes a date with YYYY-MM-DD format. Note: due_on or due_at must be present in the request when setting or unsetting the start_on parameter.
team objectCreate-only. The team that this project is shared with. This field only exists for projects in organizations.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
workspace objectCreate-only. The workspace or organization this project is associated with. Once created, projects cannot be moved to a different workspace. This attribute can only be specified at creation time.
gid stringGlobally unique identifier of the object, as a string.
resource_type stringThe base type of this resource.
name stringThe name of the object.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Detailed descriptions

html_notes: Opt In. The notes of the project with formatting as HTML. Note: This field is under active migration—please see our blog post for more information.

modified_at: The time at which this project was last modified. Note: This does not currently reflect any changes in associations such as tasks or comments that may have been added or removed from the project.

Enumerated Values

ParameterValue
colordark-pink
colordark-green
colordark-blue
colordark-red
colordark-teal
colordark-brown
colordark-orange
colordark-purple
colordark-warm-gray
colorlight-pink
colorlight-green
colorlight-blue
colorlight-red
colorlight-teal
colorlight-brown
colorlight-orange
colorlight-purple
colorlight-warm-gray
colorgreen
coloryellow
colorred
resource_subtypetext
resource_subtypeenum
resource_subtypenumber
typetext
typeenum
typenumber
default_viewlist
default_viewboard
default_viewcalendar
default_viewtimeline
layoutlist
layoutboard
section_migration_statusnot_migrated
section_migration_statusin_progress
section_migration_statuscompleted

Responses

StatusDescription
201 ProjectSuccessfully created a new project in the specified workspace.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Add a custom field to a project

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/projects/{project_gid}/addCustomFieldSetting?custom_field=14916 \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {}
}

POST /projects/{project_gid}/addCustomFieldSetting

Custom fields are associated with projects by way of custom field settings. This method creates a setting for the project.

Parameters

NameDescription
/project_gid string
required
Globally unique identifier for the project.
?custom_field string
required
The custom field to associate with this project.
?is_important booleanWhether this field should be considered "important" to this project. This may cause it to be displayed more prominently, for example in the task grid.
?insert_before stringAn id of a Custom Field Setting on this project, before which the new Custom Field Setting will be added. insert_before and insert_after parameters cannot both be specified.
?insert_after stringAn id of a Custom Field Setting on this project, after which the new Custom Field Setting will be added. insert_before and insert_after parameters cannot both be specified.
?opt_pretty booleanProvides “pretty” output.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully added the custom field to the project.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Remove a custom field from a project

Code samples

# You can also use wget
curl -X POST https://app.asana.com/api/1.0/projects/{project_gid}/removeCustomFieldSetting?custom_field=14916 \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {}
}

POST /projects/{project_gid}/removeCustomFieldSetting

Removes a custom field setting from a project.

Parameters

NameDescription
/project_gid string
required
Globally unique identifier for the project.
?custom_field string
required
The custom field to remove from this project.
?opt_pretty booleanProvides “pretty” output.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 EmptySuccessfully removed the custom field from the project.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Get task count of a project

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/projects/{project_gid}/task_counts \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

200 Response

{
  "data": {
    "num_tasks": 200,
    "num_incomplete_tasks": 50,
    "num_completed_tasks": 150,
    "num_milestones": 10,
    "num_incomplete_milestones": 7,
    "num_completed_milestones": 3
  }
}

GET /projects/{project_gid}/task_counts

Get an object that holds task count fields. All fields are excluded by default. You must opt in using opt_fields to get any information from this endpoint.

This endpoint has an additional rate limit and each field counts especially high against our cost limits.

Milestones are just tasks, so they are included in the num_tasks, num_incomplete_tasks, and num_completed_tasks counts.

Parameters

NameDescription
/project_gid string
required
Globally unique identifier for the project.
?opt_pretty booleanProvides “pretty” output.
?opt_fields array[string]Defines fields to return.
?limit integerResults per page.
?offset stringOffset token.
↓ Show Common Parameters ↓↑ Hide Common Parameters ↑

Responses

StatusDescription
200 TaskCountSuccessfully retrieved the requested project's task counts.
400 ErrorThis usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again.
401 ErrorA valid authentication token was not provided with the request, so the API could not associate a user with the request.
403 ErrorThe authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to.
404 ErrorEither the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist.
500 ErrorThere was a problem on Asana’s end. In the event of a server error the response body should contain an error phrase. These phrases can be used by Asana support to quickly look up the incident that caused the server error. Some errors are due to server load, and will not supply an error phrase.
↓ Show Common Responses ↓↑ Hide Common Responses ↑


Project Memberships

GET /project_memberships/{project_membership_gid}
GET /projects/{project_gid}/project_memberships

With the introduction of “comment-only” projects in Asana, a user’s membership in a project comes with associated permissions. These permissions (whether a user has full access to the project or comment-only access) are accessible through the project memberships endpoints described here.


Get a project membership

Code samples

# You can also use wget
curl -X GET https://app.asana.com/api/1.0/project_memberships/{project_membership_gid} \
  -H 'Accept: appli