# How to Integrate Retool with Asana

- Tool: Retool
- Difficulty: Intermediate
- Time required: 25 minutes
- Last updated: April 2026

## TL;DR

Connect Retool to Asana using a REST API Resource with a personal access token. Configure the base URL and authorization header in Retool's Resources tab, then build cross-project dashboards that aggregate tasks, track deadlines, manage assignees, and generate executive-level project status reports across all workspaces — giving operations and leadership teams views that Asana's native interface can't produce.

## Build Cross-Project Operations Dashboards with Asana and Retool

Asana is excellent for individual project management, but operations and leadership teams often need views that span multiple projects, portfolios, or workspaces simultaneously. Asana's native Portfolio and Reporting features require Enterprise plans and still have limitations for custom metrics. Retool connected to Asana's API removes these constraints: you can query tasks across every project simultaneously, calculate custom metrics with JavaScript transformers, join Asana task data with information from other systems (CRM, databases, JIRA), and display the results in rich dashboards with Charts, Gantt-style Tables, and filtered views.

Asana's REST API v1 exposes the full data model: workspaces, teams, projects, sections, tasks, subtasks, tags, users, portfolios, goals, and custom fields. Every object is identified by a globally unique ID (GID). The API's design requires you to first enumerate workspace GIDs, then project GIDs within workspaces, and finally task GIDs within projects — a hierarchical traversal that Retool handles through chained queries.

The most impactful patterns for Retool-Asana integrations are cross-project task dashboards for department heads, portfolio health reports for program managers, deadline tracking systems that alert on overdue tasks across all projects, and operations panels where support or success teams create Asana tasks directly from Retool without needing Asana licenses. These use cases are difficult or impossible to achieve from within Asana's own interface.

## Before you start

- An Asana account with access to the projects and workspaces you want to query
- An Asana personal access token generated from asana.com/0/my-apps (for individual use) or OAuth app credentials (for team deployment)
- The GID of your Asana workspace, available in your Asana URL or via the /workspaces endpoint
- A Retool account with permission to create Resources
- Knowledge of which Asana projects and portfolios you want to surface in Retool

## Step-by-step guide

### 1. Generate an Asana personal access token

Go to asana.com and sign in. Click your profile picture in the top right, select 'My Profile Settings', then click the 'Apps' tab. Scroll down to 'Developer Apps' and click 'Manage Developer Apps'. Click 'New Access Token', give it a descriptive name like 'Retool Dashboard', and click 'Create Token'.

Asana displays the access token once — copy it immediately and store it in a password manager. This token authenticates as you and has access to everything your Asana user account can see: all workspaces, teams, projects, and tasks you're a member of or have been granted access to.

For team deployments where multiple Retool users need access to shared Asana projects, consider using Asana's OAuth 2.0 app authentication instead. This requires creating an Asana developer application at asana.com/0/developer-console, configuring OAuth credentials, and setting up per-user authentication in Retool. For most internal tool use cases, a shared service account token (if permitted by your Asana admin) is simpler and more practical.

Note your workspace GID as well — you'll need it for queries that require workspace context. To find it, open any Asana task URL: the number appearing as the first segment after 'app.asana.com/0/' is your workspace GID.

**Expected result:** You have an Asana personal access token copied and stored securely. You know your workspace GID for scoping queries.

### 2. Configure the Asana REST API Resource in Retool

In Retool, go to the Resources tab and click 'Create a resource'. Select 'REST API'. Name it 'Asana API'.

Set the base URL to https://app.asana.com/api/1.0. This is Asana's stable API v1 base URL — all query paths are relative to this.

For authentication, select 'Bearer Token'. Paste your Asana personal access token in the token field. Retool will send Authorization: Bearer {token} with every request.

Add a custom header to help Asana format responses correctly. Click 'Add header' and set the name to asana-enable with the value new_user_task_lists,new_project_templates. This enables newer API behaviors for the Asana API and prevents deprecation warnings in responses. Also add Accept: application/json to ensure JSON responses.

Save the resource. Since personal access tokens don't expire, there's no OAuth flow to complete — the resource is ready to use immediately.

For team environments, store the Asana token in a Retool configuration variable (Settings → Configuration Variables, mark as secret) and reference it in the Bearer Token field as {{ retoolContext.configVars.ASANA_TOKEN }}. This lets you rotate the token without updating every Resource.

**Expected result:** The Asana API resource is configured in Retool with Bearer authentication. The resource is ready to use without any OAuth flow.

### 3. Fetch workspaces and projects to understand the data model

Start by querying Asana's top-level resources to understand what's available. Create a query named 'getWorkspaces' with GET method and path /workspaces. This returns all workspaces your token can access. Run it and note the GID of the workspace you want to query — copy the GID value for use in subsequent queries.

Next, create a query 'getProjects' with GET method and path /projects. Add URL parameters: workspace (set to your workspace GID), archived (set to false to exclude archived projects), and opt_fields (set to gid,name,color,current_status,due_on,owner,owner.name,permalink_url). The opt_fields parameter is critical for Asana — without it, most queries return only GID and name, and additional fields require a separate request per object.

Asana's API returns paginated results using a cursor-based pagination system. The response body has a 'data' array and optionally a 'next_page' object with an 'offset' cursor string. Add a limit parameter set to 100 (Asana's maximum) and implement pagination by passing the offset cursor in subsequent requests.

Create a third query 'getProjectTasks' with GET method and path /tasks. Add URL parameters: project (set to {{ projectsTable.selectedRow?.gid || '' }}), completed_since (set to 'now' to get incomplete tasks or a specific date string), and opt_fields (set to gid,name,assignee,assignee.name,due_on,completed,notes,custom_fields,memberships,tags,permalink_url). Run this query after selecting a project row.

```
// JavaScript transformer for Asana tasks response
const tasks = data.data || [];

return tasks.map(task => {
  // Extract custom field values (custom fields vary by project)
  const customFields = {};
  (task.custom_fields || []).forEach(cf => {
    const value = cf.type === 'enum' ? cf.enum_value?.name
      : cf.type === 'number' ? cf.number_value
      : cf.type === 'text' ? cf.text_value
      : cf.type === 'date' ? cf.date_value
      : cf.display_value;
    customFields[cf.name] = value || '';
  });
  
  const dueDate = task.due_on ? new Date(task.due_on) : null;
  const today = new Date();
  const isOverdue = dueDate && !task.completed && dueDate < today;
  const daysUntilDue = dueDate
    ? Math.ceil((dueDate - today) / (1000 * 60 * 60 * 24))
    : null;

  return {
    gid: task.gid,
    name: task.name,
    assignee: task.assignee?.name || 'Unassigned',
    dueOn: task.due_on || '',
    daysUntilDue,
    isOverdue,
    completed: task.completed,
    priority: customFields['Priority'] || '',
    status: customFields['Status'] || '',
    notes: task.notes ? task.notes.substring(0, 100) + (task.notes.length > 100 ? '...' : '') : '',
    url: task.permalink_url
  };
});
```

**Expected result:** You can query workspaces, projects, and tasks. The transformer flattens Asana's nested structure into display-ready objects. Custom field values are accessible by name.

### 4. Build the cross-project task overview

Build a multi-project task dashboard. The challenge is that Asana's task endpoint queries one project at a time — to build a cross-project view, you need to query multiple projects and aggregate results using a JavaScript query.

Create a JavaScript query named 'getAllProjectTasks'. In the query body, use Promise.all() to query tasks from multiple project GIDs simultaneously, then merge and sort the results. Reference the projects query to get all project GIDs dynamically.

For the UI, add a Select component at the top for filtering by project, an Assignee Select filter, a Date Range picker for due date filtering, and a Toggle for 'Show overdue only'. Connect these filters to your task transformer using {{ }} expressions.

Add a Table component showing the transformed task data. Configure columns: name (with a link to task.url), project (if showing cross-project data), assignee (as a Tag), dueOn (formatted), isOverdue (as conditional row styling — red background for overdue tasks), priority, and status.

Above the table, add three Statistic components: Total Tasks, Overdue Tasks (tasks where isOverdue is true), and Tasks Due This Week. These give the executive-level summary at a glance.

For charts, add a Bar Chart showing task count by assignee, and a second chart showing tasks by due date distribution to visualize workload concentration.

```
// JavaScript query to aggregate tasks across multiple projects
const projectGids = getProjects.data?.data?.map(p => p.gid) || [];

if (projectGids.length === 0) return [];

// Query tasks for each project in parallel (up to 10 projects)
const projectsToQuery = projectGids.slice(0, 10);

const results = await Promise.all(
  projectsToQuery.map(projectGid =>
    getProjectTasks.trigger({ additionalScope: { overrideProjectGid: projectGid } })
      .then(result => (result.data || []).map(task => ({
        ...task,
        projectGid,
        projectName: getProjects.data?.data?.find(p => p.gid === projectGid)?.name || ''
      })))
      .catch(() => []) // Handle individual project failures gracefully
  )
);

// Flatten all project results into one array and sort by due date
return results.flat().sort((a, b) => {
  if (!a.due_on) return 1;
  if (!b.due_on) return -1;
  return new Date(a.due_on) - new Date(b.due_on);
});
```

**Expected result:** The dashboard shows tasks aggregated across multiple Asana projects with filtering by project, assignee, and due date. Overdue tasks are highlighted in red. Summary statistics show total, overdue, and upcoming tasks.

### 5. Add task creation and update capabilities

Enable the Retool dashboard to create and update Asana tasks. Create a query named 'createTask' with POST method and path /tasks. The request body must include the workspace or project GID, task name, and optional properties.

Build a Retool Form component for task creation. Add fields: Task Name (TextInput, required), Project (Select, populated from getProjects), Section (Select, populated based on project selection), Assignee (Select, populated from team members), Due Date (DatePicker), Priority (Select for custom field), and Description (TextArea). Connect the Form's submit button to the createTask query.

For task updates, add inline editing to the tasks Table component: enable 'Save changes' mode and create an 'updateTask' query with PUT method and path /tasks/{{ tasksTable.selectedRow?.gid }}. The update body only needs the changed fields — Asana supports partial updates.

For marking tasks complete, add a 'Mark Complete' action button in the table. This triggers a PUT to /tasks/{gid} with body { "completed": true }. Add a confirmation modal since completing tasks in Asana notifies assignees and followers.

For complex Asana integrations with portfolio management, custom fields, rules automation, and cross-workspace reporting, RapidDev's team can help build the complete operations system.

```
// POST /tasks — Create a new Asana task
{
  "data": {
    "name": "{{ taskNameInput.value }}",
    "notes": "{{ taskDescriptionInput.value }}",
    "assignee": "{{ assigneeSelect.value || null }}",
    "due_on": "{{ taskDueDatePicker.value || null }}",
    "projects": ["{{ projectSelect.value }}"],
    "memberships": [{
      "project": "{{ projectSelect.value }}",
      "section": "{{ sectionSelect.value || null }}"
    }],
    "custom_fields": {
      "{{ priorityFieldGid }}": "{{ prioritySelect.value }}"
    }
  }
}
```

**Expected result:** The Retool app can create new Asana tasks via a form, mark tasks complete, and update task properties inline. New tasks appear in Asana immediately with all required properties set.

## Best practices

- Always use the opt_fields parameter in Asana queries — it's the single most important optimization for performance and rate limit efficiency; specify only the fields your UI actually displays
- Store frequently-queried workspace and project GIDs in Retool configuration variables rather than hardcoding them in query parameters — this makes the app easier to configure for different teams
- Use Retool query caching with a 5-minute TTL for project and user lists that change infrequently — task lists can have shorter or no caching if real-time accuracy is important
- Implement a 'last updated' indicator showing when cross-project task data was last fetched, and add a manual 'Refresh' button rather than auto-refreshing frequently to stay within rate limits
- For task creation forms, pre-populate the Project and Assignee selects using live Asana data rather than hardcoding options — this ensures the form stays current as projects and team members change
- Use Asana's search API (/tasks/search with text parameter) for full-text task search across the workspace rather than loading all tasks and filtering in JavaScript — this is significantly faster for large workspaces
- When building dashboards for multiple teams using the same Retool app, parameterize the workspace and project GIDs via URL parameters so different team URLs load different Asana projects automatically

## Use cases

### Executive cross-portfolio project status dashboard

Build a dashboard that queries all portfolios visible to the authenticated user, calculates completion percentages and overdue task counts for each portfolio, and displays a grid of project health cards showing RAG (red/amber/green) status. Leadership gets a real-time view of all programs without navigating through Asana's portfolio hierarchy.

Prompt example:

```
Build a Retool app querying all Asana portfolios and their contained projects. Calculate completion percentage (completed tasks / total tasks) and overdue task count per project. Display a Table with project name, portfolio, owner, deadline, completion %, overdue count, and a status tag (green/yellow/red). Add a Date Range filter and click-through to project task details.
```

### Department task backlog and assignment panel

Build a team task management panel that shows all incomplete tasks assigned to team members across all department projects. Group by assignee, show due dates, enable bulk reassignment, and allow filtering by project or section. This gives managers visibility into individual workloads without Asana's premium seats.

Prompt example:

```
Build a Retool dashboard querying all tasks in a specific Asana workspace assigned to team members in the Engineering department. Group by assignee and show total task count, overdue count, and a Table of tasks with name, project, due date, priority, and a reassign button. Include a 'Create Task' form.
```

### Operations request intake and task creation panel

Build an internal request intake form where non-technical staff submit work requests that automatically create Asana tasks with the correct project, section, assignee, and custom fields pre-populated. This eliminates direct Asana access for large teams while maintaining task tracking through Asana.

Prompt example:

```
Build a Retool form for submitting IT support requests. Fields: request type (Select), description, priority, requested deadline, and requester name. On submit, create an Asana task in the IT Operations project, assign to the correct queue based on type, and show a confirmation with the task URL. Display a 'My Recent Requests' table showing past submissions.
```

## Troubleshooting

### Asana API queries return only GID and name for tasks, missing fields like assignee, due_on, and custom_fields

Cause: Asana's API returns only GID and name by default unless you specify additional fields using the opt_fields parameter.

Solution: Add the opt_fields URL parameter to your task queries listing every field you need, comma-separated. For a comprehensive task view, use: gid,name,assignee,assignee.name,due_on,completed,notes,custom_fields,custom_fields.name,custom_fields.display_value,permalink_url. Without opt_fields, Asana's API is intentionally minimal to reduce payload size.

### Asana returns 429 Too Many Requests when loading a cross-project dashboard

Cause: Querying tasks from many projects simultaneously exceeds Asana's rate limit of 1,500 requests/minute for personal access tokens or 150 requests/minute for OAuth apps.

Solution: Limit parallel project queries to 5-10 projects per page load. Add a Select component to let users choose which projects to load rather than loading all at once. For dashboards requiring data from many projects, use a Retool Workflow on a schedule (every 15 minutes) to pre-fetch task data into a Retool Database table and serve the dashboard from cached data.

### Creating a task returns 400 Bad Request with 'Invalid Request: No workspace or organization provided'

Cause: Task creation requires either a workspace GID, a project GID (which implies the workspace), or an assignee (which Asana can infer the workspace from). Without at least one of these, Asana can't determine which workspace to create the task in.

Solution: Include at least one of: the workspace parameter (workspace GID), the projects array (with at least one project GID), or the assignee (a user GID within a known workspace). The simplest fix for a form-based task creator is always requiring the project selection and including it in the request body.

```
// Minimum required body for task creation
{
  "data": {
    "name": "{{ taskNameInput.value }}",
    "projects": ["{{ projectSelect.value }}"]
  }
}
```

### Asana pagination only returns the first 100 tasks even when a project has more

Cause: Asana paginates results with a maximum of 100 items per page. Without handling the next_page cursor in responses, only the first page is displayed.

Solution: Check the query response for a 'next_page' object containing an 'offset' string. Pass this offset as a URL parameter in the next request to retrieve subsequent pages. For complete datasets, implement recursive fetching in a Retool Workflow or JavaScript query, accumulating results until next_page is null. For dashboards, implement explicit 'Load more' pagination rather than loading all records at once.

## Frequently asked questions

### Can I use a single Asana personal access token for a shared Retool team dashboard?

Yes. If you configure the Asana Resource with 'Share credentials between users' enabled and use a service account's token, all Retool users will authenticate as that service account. Ensure the service account has member access to all required Asana workspaces and projects. Personal access tokens don't expire, so this approach works well for production dashboards — just rotate the token periodically for security.

### Does Retool's Asana integration support Asana Portfolios and Goals?

Yes, Asana's API exposes portfolios via the /portfolios endpoint and goals via the /goals endpoint. Both require querying by workspace GID first. Portfolio items (projects within portfolios) are accessed via /portfolios/{portfolio_gid}/items. You can build executive dashboard views over portfolios using the same REST API Resource — add opt_fields to include portfolio-level custom fields and status updates.

### Why does Asana's API require so many separate requests to get complete data?

Asana's API is designed for efficiency — by default it returns minimal fields to reduce payload size and server load. The opt_fields system allows clients to request exactly the data they need. While this requires more thought in query design, it means Asana queries are faster and consume less rate limit than APIs that return complete objects regardless of what the client needs. Use opt_fields carefully and you'll find the performance excellent.

### Can I trigger Asana rules or automations through the API?

Creating or updating tasks through Asana's API will trigger any rules set up in Asana that respond to those events (e.g., 'When task is created → assign to team member' or 'When due date passes → add tag'). You don't need to explicitly trigger rules — they fire based on the data changes your API calls make. However, you cannot directly invoke Asana rules via the API.

---

Source: https://www.rapidevelopers.com/retool-integrations/asana
© RapidDev — https://www.rapidevelopers.com/retool-integrations/asana
