Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsFlutterFlow API Call

Asana

Connect FlutterFlow to Asana using a FlutterFlow API Call with a Bearer personal access token. Create an API Group pointing at app.asana.com/api/1.0, set the Authorization header at the group level, then chain GET /workspaces and GET /projects to capture GIDs before fetching tasks. Always include the opt_fields query parameter — without it, Asana returns only gid and name, leaving date and assignee bindings silently null.

What you'll learn

  • How to generate an Asana personal access token and configure a Bearer auth API Group in FlutterFlow
  • Why every Asana response is wrapped in a 'data' envelope and how to adjust your JSON Path bindings accordingly
  • How to use the opt_fields query parameter to request specific task fields — the most common Asana API mistake
  • How to chain GET /workspaces and GET /projects calls to capture GIDs and store them in App State
  • How to handle the 1,500 req/min rate limit when your app displays multiple projects at once
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate15 min read50 minutesProductivityLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to Asana using a FlutterFlow API Call with a Bearer personal access token. Create an API Group pointing at app.asana.com/api/1.0, set the Authorization header at the group level, then chain GET /workspaces and GET /projects to capture GIDs before fetching tasks. Always include the opt_fields query parameter — without it, Asana returns only gid and name, leaving date and assignee bindings silently null.

Quick facts about this guide
FactValue
ToolAsana
CategoryProductivity
MethodFlutterFlow API Call
DifficultyIntermediate
Time required50 minutes
Last updatedJuly 2026

Build a Team Task App on Asana Data in FlutterFlow

Asana's REST API v1 gives you full access to your workspace's tasks, projects, teams, and users — making it ideal for building a custom mobile interface on top of your existing Asana data. Whether you're building a client-facing progress tracker, a focused team sprint view, or a personal task companion, the Asana API provides the building blocks.

Asana's REST API is available on all plan tiers including Free. Authentication uses Bearer tokens — you generate a personal access token (PAT) from asana.com/0/my-apps and include it in an Authorization header. PATs are scoped to your individual Asana account and support up to 1,500 requests per minute, which is generous for most mobile apps.

The most important Asana-specific concept to understand before you build: every API response is wrapped in a 'data' envelope. A GET /tasks call doesn't return an array directly — it returns { "data": [...tasks...] }. If you configure JSON Path bindings without the .data prefix (binding $[*].name instead of $.data[*].name), your FlutterFlow ListView will always be empty. Additionally, without the opt_fields query parameter, Asana returns only the gid and name fields — any binding for due_on, assignee, completed, or other fields will silently return null. These two patterns — the data envelope and opt_fields — are the #1 and #2 sources of confusion when connecting FlutterFlow to Asana.

Integration method

FlutterFlow API Call

FlutterFlow connects to Asana's REST API v1 using Bearer authentication with a personal access token. Every Asana API response is wrapped in a 'data' envelope and objects are identified by opaque GID strings rather than names. You chain GET calls to capture workspace and project GIDs, then fetch tasks using the opt_fields query parameter to request the specific fields you want to display in your FlutterFlow widgets.

Prerequisites

  • An Asana account (any plan including Free) with access to the workspace you want to display
  • An Asana personal access token generated at asana.com/0/my-apps → New Access Token
  • The GID of the workspace or project you want to display (you'll fetch these via API in the tutorial)
  • A FlutterFlow project with the API Calls panel available
  • A Firebase project if you want to protect write operations (task creation, completion) via Cloud Functions

Step-by-step guide

1

Generate your Asana personal access token

Navigate to asana.com/0/my-apps in your browser. This is Asana's developer portal where you manage personal access tokens and OAuth apps. Click New Access Token, give it a label like 'FlutterFlow integration', and click Create. Copy the token immediately — Asana only shows it once at creation time. The personal access token (PAT) works for all Asana REST v1 endpoints and is tied to your individual account. It supports up to 1,500 requests per minute for PATs (compared to 150/minute for OAuth apps). This generous limit means most FlutterFlow apps won't hit rate limits unless they fan out across many projects simultaneously. The PAT inherits your account's permissions — it can access every workspace, project, and task you can see in Asana. This means it's a broadly scoped credential. For read-only FlutterFlow features (browsing tasks and projects), storing it in App Constants is reasonable. For write operations like creating or completing tasks, route those through a backend proxy to prevent the token from being extractable from the compiled app. Also note: PATs don't expire by default, but you can revoke them at any time from asana.com/0/my-apps. If you suspect the token has been exposed, revoke it immediately and generate a new one.

Pro tip: Copy the PAT immediately after creating it. Asana only shows it once. If you lose it, you must revoke it and generate a new one.

Expected result: You have your Asana personal access token copied and ready to use in FlutterFlow.

2

Create the Asana API Group with Bearer auth

In FlutterFlow, click API Calls in the left navigation sidebar. Click + Add → Create API Group. Name the group Asana. Set the Base URL to https://app.asana.com/api/1.0 — this is the base for all Asana REST v1 endpoints. No trailing slash. Add the group-level Authorization header. In the Headers section, click Add Header. Set the key to Authorization and the value to Bearer YOUR_PAT — replace YOUR_PAT with your actual token. Alternatively, go to Settings → App Values (App Constants), create a constant named ASANA_TOKEN with your token value, and set the header to Bearer {{ASANA_TOKEN}} to keep the token in one configurable place. All child API Calls inside this group will inherit this Authorization header automatically. You'll never need to set it on individual calls. Save the group. Now you have a base configuration that all Asana calls will share. The next step is to make your first API call to discover workspace GIDs — Asana's way of identifying objects.

api_group_config.txt
1// API Group configuration reference
2// Group name: Asana
3// Base URL: https://app.asana.com/api/1.0
4// Headers (inherited by all child calls):
5// Authorization: Bearer {{ASANA_TOKEN}}
6// Accept: application/json

Pro tip: Store your PAT as an App Constant named ASANA_TOKEN and reference it as Bearer {{ASANA_TOKEN}} in the header — this makes it easy to rotate the token without hunting through individual call configurations.

Expected result: An Asana API Group is saved in FlutterFlow with the base URL and Bearer Authorization header configured.

3

Fetch workspaces and projects to capture GIDs

Asana identifies every object — workspaces, projects, tasks, users — by an opaque GID (Global ID) string, not by name. Before you can fetch tasks from a specific project, you need the project's GID. The cleanest approach is to fetch it once via API and store it in App State. Add a new API Call inside the Asana group. Name it GetWorkspaces. Method: GET. Endpoint: /workspaces?opt_fields=name,gid. Test this call — you'll see a response like { "data": [{ "gid": "1234567890", "name": "My Workspace" }] }. Note the .data envelope. Copy the GID of your target workspace from the test response. Next, add another API Call. Name it GetProjects. Method: GET. Endpoint: /projects?workspace={{workspaceGid}}&opt_fields=name,gid. Add a variable named workspaceGid. Test it with your workspace GID — you'll see all projects in that workspace. In your FlutterFlow app, create an App State variable named currentWorkspaceGid (String) and another named currentProjectGid (String). On your home page, add a page-load action that calls GetWorkspaces, then sets currentWorkspaceGid from the first item's GID ($.data[0].gid). If the user might have multiple workspaces, build a picker page. Similarly, set currentProjectGid when the user selects a project. By storing GIDs in App State, you avoid re-fetching parent objects every time the user navigates between pages — a common source of unnecessary API calls.

workspace_projects_response.json
1// GET /workspaces response structure
2{
3 "data": [
4 {
5 "gid": "1234567890123456",
6 "name": "My Company Workspace",
7 "resource_type": "workspace"
8 }
9 ]
10}
11// JSON Path for GID: $.data[0].gid
12// JSON Path for name: $.data[0].name
13
14// GET /projects?workspace={gid} response structure
15{
16 "data": [
17 {
18 "gid": "9876543210987654",
19 "name": "Mobile App Redesign",
20 "resource_type": "project"
21 }
22 ]
23}
24// JSON Path for list: $.data[*].gid and $.data[*].name

Pro tip: Workspace and project GIDs don't change, so you only need to fetch them once per session. Store them in App State on first load and reuse them for all subsequent task calls.

Expected result: GetWorkspaces and GetProjects API Calls are configured and tested successfully. You have your workspace and project GIDs stored in App State variables.

4

Fetch tasks with opt_fields and bind to a ListView

Now add the core API Call. Name it GetTasks. Method: GET. Endpoint: /tasks?project={{projectGid}}&opt_fields=name,due_on,assignee.name,completed,notes. Add a variable named projectGid of type String. The opt_fields parameter is critical. Without it, Asana returns only { "gid": "...", "name": "..." } for each task — every other field (due_on, assignee, completed, notes) will be completely absent from the response. There's no error; the fields are just not there. Binding $.data[*].due_on to a Text widget will show nothing, silently. Always include every field you plan to display in opt_fields. Go to the Response & Test tab. Set the projectGid variable to your project GID and click Test API Call. You should see tasks with the requested fields. Click Generate from Response to create JSON Paths. Key JSON Paths to map (note the $.data[*] prefix, not $[*]): - Task name: $.data[*].name - Due date: $.data[*].due_on - Assignee: $.data[*].assignee.name - Completed: $.data[*].completed (boolean) - Task GID: $.data[*].gid On your tasks page in FlutterFlow, add a ListView. Set its Backend Query to the Asana group → GetTasks, passing currentProjectGid from App State as the projectGid variable. Inside the ListView, add Card and Text widgets, binding each to the appropriate JSON Paths. For the due date, bind $.data[*].due_on — guard it with a conditional because tasks without a due date will have null here.

tasks_response.json
1// GET /tasks request — opt_fields is REQUIRED
2// GET /tasks?project={projectGid}&opt_fields=name,due_on,assignee.name,completed,notes,gid
3
4// Sample response (paste into Response & Test → Generate from Response)
5{
6 "data": [
7 {
8 "gid": "1234567890",
9 "name": "Write onboarding copy",
10 "due_on": "2026-07-15",
11 "completed": false,
12 "notes": "Review with marketing before finalizing",
13 "assignee": {
14 "gid": "9876543210",
15 "name": "Jane Doe"
16 },
17 "resource_type": "task"
18 },
19 {
20 "gid": "1234567891",
21 "name": "Design login screen",
22 "due_on": null,
23 "completed": true,
24 "notes": "",
25 "assignee": null
26 }
27 ]
28}
29
30// Key JSON Paths
31// $.data[*].name → task name
32// $.data[*].due_on → due date (string YYYY-MM-DD, can be null)
33// $.data[*].assignee.name → assignee (can be null if unassigned)
34// $.data[*].completed → boolean
35// $.data[*].gid → task GID (needed for updates)

Pro tip: If your ListView shows task names but due dates and assignees are always blank, you've missed opt_fields. Add ?opt_fields=name,due_on,assignee.name,completed to the endpoint and regenerate JSON Paths.

Expected result: The GetTasks API Call returns tasks with name, due_on, assignee.name, and completed fields. A ListView on your tasks page displays them with correct data bindings.

5

Create tasks and mark complete via secure API calls

Completing a task requires a PUT request to /tasks/{gid} with a body of {"data":{"completed":true}}. Creating a task requires a POST to /tasks with a body containing workspace GID, name, assignee, and project. Both are write operations. For an app with a small, trusted user base (e.g., an internal tool where users log in with your company Asana accounts), you might accept the security trade-off and make these calls directly from FlutterFlow. For consumer-facing apps, route writes through a Firebase Cloud Function or Supabase Edge Function to protect the PAT. If you're making direct FlutterFlow calls, add a CompleteTask API Call in the Asana group. Method: PUT. Endpoint: /tasks/{{taskGid}}. Add a taskGid variable. Set the body to {"data": {"completed": true}}. In your task ListView, add a Checkbox or IconButton to each task row. Wire its Action to Backend/API Call → CompleteTask, passing the current row's $.data[*].gid as taskGid. After a successful response, trigger a page refresh (or update the local App State to reflect the completion without a full refresh, for a snappier UX). For task creation, build a bottom sheet or new page with text fields for task name and due date. POST to /tasks with a body: {"data": {"name": "{{taskName}}", "projects": ["{{projectGid}}"], "workspace": "{{workspaceGid}}", "due_on": "{{dueDate}}", "assignee": "me"}}. The word 'me' in the assignee field automatically assigns to the token owner. Be mindful of the 1,500 req/min rate limit if you have many simultaneous users. If your app powers a team of 20 people all checking tasks at once, you may be sending 20 requests per page load — still well within limits for most team sizes.

task_write_config.json
1// PUT /tasks/{taskGid} — mark task complete
2// Method: PUT
3// Endpoint: /tasks/{{taskGid}}
4// Body:
5{
6 "data": {
7 "completed": true
8 }
9}
10
11// POST /tasks — create a new task
12// Method: POST
13// Endpoint: /tasks
14// Body:
15{
16 "data": {
17 "name": "{{taskName}}",
18 "notes": "{{taskNotes}}",
19 "due_on": "{{dueDate}}",
20 "projects": ["{{projectGid}}"],
21 "workspace": "{{workspaceGid}}",
22 "assignee": "me"
23 }
24}
25// Note: all body values are also wrapped in the 'data' envelope for writes

Pro tip: If you'd rather skip the backend setup and have RapidDev build this integration for you, we do FlutterFlow + Asana integrations regularly — free scoping call at rapidevelopers.com/contact.

Expected result: Tasks can be marked complete from the ListView and new tasks can be created from a form. The task list refreshes to reflect changes.

Common use cases

Team task dashboard for a cross-functional project

Build a mobile app where team members can see all open tasks in an Asana project, filtered by their own assignee. The app fetches tasks from a specific project GID with opt_fields including name, due_on, assignee.name, and completed. Tasks are displayed in a ListView grouped by due date with overdue items highlighted in red.

FlutterFlow Prompt

Show all incomplete tasks in my Asana project assigned to me, sorted by due date, with task name, due date, and a checkbox to mark complete.

Copy this prompt to try it in FlutterFlow

Client-facing project progress tracker

Build a read-only app for clients to check project progress. The app fetches all tasks in a designated client project, counts total vs completed tasks, and shows a progress bar plus a list of recent completions. The client can see what's done and what's in flight without needing a Jira account.

FlutterFlow Prompt

Display an Asana project's overall completion percentage and a list of tasks completed in the last 7 days with task name and completion date.

Copy this prompt to try it in FlutterFlow

Personal daily task companion app

Build a focused personal task app that shows only tasks assigned to the current user and due today or earlier. The app calls GET /tasks?assignee=me&workspace={gid}&due_on.before=today with opt_fields for name, due_on, projects.name and presents a compact checklist. Completing a task sends a PUT request to mark it done.

FlutterFlow Prompt

Show all Asana tasks assigned to me that are due today or overdue, with task name, project name, and a button to mark each one complete.

Copy this prompt to try it in FlutterFlow

Troubleshooting

ListView shows task names but due dates, assignees, and other fields are always blank

Cause: The opt_fields query parameter is missing or doesn't include the fields being bound. Asana only returns gid and name by default; every additional field must be explicitly requested via opt_fields.

Solution: Update your GetTasks API Call endpoint to include opt_fields: /tasks?project={{projectGid}}&opt_fields=name,due_on,assignee.name,completed,notes,gid. Click Test API Call to confirm the new fields appear in the response, then regenerate JSON Paths.

ListView is always empty despite the API returning data in the Response tab

Cause: JSON Path bindings are missing the .data prefix. Asana wraps all responses in a 'data' envelope: { "data": [...] }. Bindings starting with $[*] instead of $.data[*] won't match any data.

Solution: Check all your JSON Path bindings for this API Call. Change $[*].name to $.data[*].name, $[*].gid to $.data[*].gid, and so on for every field. Re-generate JSON Paths from the Response & Test tab to ensure they start with $.data[*].

429 Too Many Requests error when loading a dashboard with multiple projects

Cause: The app is making one task-fetch call per project on page load. With 10+ projects, this fans out to 10+ concurrent requests, which can be a risk at high traffic volumes even with the 1,500 req/min PAT limit.

Solution: Load project GIDs into a selector/picker first and only fetch tasks for the project the user actively selects. Store loaded task lists in App State so navigating away and back doesn't re-fetch. Show a 'Tap to load' button rather than auto-loading all projects at once.

401 Unauthorized — token appears correct

Cause: The PAT may have been revoked, the Bearer prefix may be missing, or there's a trailing space or newline in the token value copied from Asana's UI.

Solution: Go to asana.com/0/my-apps, revoke the old token, and create a new one. Paste the new token carefully without trailing spaces. In your App Constant, ensure the Authorization header value is exactly: Bearer [token] — the word Bearer, a single space, then the token with no surrounding spaces or newlines.

Best practices

  • Always include opt_fields in every task/project API Call — omitting it returns only gid and name, and missing field bindings fail silently without any error message.
  • Remember the 'data' envelope: every Asana response wraps results in { "data": ... } so all JSON Path bindings must start with $.data, not $[*] or $.results.
  • Fetch workspace and project GIDs once on app load and store them in App State — GIDs don't change, so re-fetching them on every navigation is wasteful.
  • Guard all nullable field bindings (due_on, assignee) with Conditional Widgets so null values show a placeholder ('No due date', 'Unassigned') rather than a blank or crash.
  • Use server-side filtering via the project and assignee query parameters rather than fetching all tasks and filtering in Dart — this keeps response sizes small and respects rate limits.
  • When building for a team, test with 5-10 simultaneous simulated page loads to confirm your request rate stays comfortably below the 1,500 req/min limit per PAT.
  • Display task GIDs in App State when navigating to detail pages so you can construct PUT/DELETE endpoints without an extra lookup call.
  • Avoid polling Asana on a tight interval; use explicit pull-to-refresh gestures instead to keep request counts low.

Alternatives

Frequently asked questions

Why does FlutterFlow show empty task lists when the API clearly returns data?

The most likely cause is incorrect JSON Path bindings. Every Asana response is wrapped in a 'data' key — a task list returns { "data": [ {...}, {...} ] }, not a bare array. Your JSON Path bindings must start with $.data[*] not $[*]. Open the Response & Test tab for your GetTasks call, paste a sample response, click Generate from Response, and FlutterFlow will generate the correct paths automatically.

What is opt_fields and why is it required?

opt_fields is an Asana API query parameter that tells the server which fields to include in the response. Without it, Asana returns only the gid and name of each object to minimize payload size. If you want due_on, assignee.name, completed, or notes in your response, you must request them explicitly: ?opt_fields=name,due_on,assignee.name,completed. Missing opt_fields causes silent null values in your UI — no error, just empty fields.

Can I connect FlutterFlow to Asana without writing any custom code?

Yes, for read-only operations. The API Group with Bearer auth and GET calls to /workspaces, /projects, and /tasks can be set up entirely through FlutterFlow's visual API Calls panel without any Dart code. You only need a Custom Function if you want to format dates or handle nested opt_fields responses. Write operations (creating or completing tasks) can also be done directly in the API Calls panel, though routing them through a Cloud Function is recommended for security.

Does this integration work on both iOS/Android and web FlutterFlow builds?

Yes, for mobile builds (iOS and Android). For web builds, browser CORS policies may block direct calls to app.asana.com. Asana's API does include CORS headers for common origins, but if you encounter XMLHttpRequest errors on your published web build, route API calls through a Firebase Cloud Function that sets permissive CORS headers.

How do I show tasks from multiple Asana projects on one screen?

Make one GetTasks call per project and merge the results in App State. Use a Sequential action to call GetProjects, iterate through the results, call GetTasks for each project GID, and append results to an App State list. Be mindful of the 1,500 req/min rate limit — fetching 15 projects at once is 15 calls which is still well within limits, but avoid running this sequence on a polling timer.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.