Connect FlutterFlow to monday.com using a FlutterFlow API Call that POSTs GraphQL queries to the single endpoint https://api.monday.com/v2. monday.com's API is GraphQL — not REST — so you define one API Call with a query variable rather than multiple REST endpoints. Keep the API token in a Cloud Function proxy; it grants broad account access and must not ship inside the app bundle.
| Fact | Value |
|---|---|
| Tool | Monday.com |
| Category | Productivity |
| Method | FlutterFlow API Call |
| Difficulty | Intermediate |
| Time required | 60 minutes |
| Last updated | July 2026 |
monday.com in a FlutterFlow App: The GraphQL Difference
Monday.com is built differently from most REST-style tools you may have connected to FlutterFlow. Its entire API lives at a single URL — https://api.monday.com/v2 — and every request is a POST with a GraphQL query string in the body. There is no GET /boards, no GET /items, no per-resource REST path. Instead, you define what data you want in a query string, send it as a POST body, and monday.com returns exactly that data. This approach is more powerful than REST for complex reads (you can fetch boards plus their items plus column definitions in a single round trip), but it requires a different mental model in FlutterFlow: you configure one API Call and change the query variable, rather than setting up a separate Call per endpoint.
The API token is generated in monday.com under Admin → API. It is a static credential that grants access to your entire monday.com account — including all boards, items, and user data. Because FlutterFlow compiles to a client app (iOS, Android, or web), any token placed directly in an API Call header is embedded in the compiled app bundle and can be extracted by anyone who decompiles the binary. This is the same risk that applies to all write-scope tokens in FlutterFlow, and the solution is always the same: deploy a Firebase Cloud Function or Supabase Edge Function that holds the token server-side, and have FlutterFlow call the function URL instead of monday.com directly.
Monday.com's API has a rate limit of approximately 60 requests per minute on most plans, with an additional complexity budget based on how many nested objects your query fetches. Batching your board and item reads into a single nested GraphQL query — rather than making separate calls for boards, then items, then columns — is both more efficient and less likely to hit the rate ceiling. Paid Work OS plans include API access; check monday.com's current plan pages to confirm API availability for your tier.
Integration method
Monday.com exposes a single GraphQL v2 endpoint at https://api.monday.com/v2 — every request is a POST with a GraphQL query or mutation string in the request body. In FlutterFlow, you create one API Call that sends a variable-driven GraphQL query and receives board/item JSON in response. Because the API token grants broad account access, it must never live in a FlutterFlow API Call header where it would be compiled into the app bundle. A Firebase Cloud Function or Supabase Edge Function holds the token, proxies the GraphQL POST to monday.com, and returns cleaned JSON that FlutterFlow can bind directly.
Prerequisites
- A monday.com account with a board containing real data (free trial is sufficient to test)
- A Google Cloud or Firebase project for deploying a Cloud Function (free tier covers testing)
- A FlutterFlow account with a project set up
- Basic familiarity with JSON — GraphQL responses are nested JSON objects
- Node.js installed locally for writing and deploying the Cloud Function (or use the Firebase Console's inline editor)
Step-by-step guide
Generate a monday.com API v2 token
Log in to your monday.com account and click your avatar in the top-right corner of the screen, then click Administration (you must be an admin on the account). In the left sidebar of the Admin panel, click the API section. You will see a Personal API Token for your account — this is the v2 token used for all API calls. Click 'Regenerate' to get a fresh token if one does not exist, or copy the existing token. The token is a long alphanumeric string. Copy it and store it somewhere safe for the next step — you will paste it into your Cloud Function's environment variables, not into FlutterFlow. Do not paste it into any FlutterFlow field at this stage. Understand what this token grants: it has access to all boards, items, updates, and user data in your monday.com account. In the context of a client app like FlutterFlow, this means it must never appear in an API Call header, an App State variable, or any field that is compiled into the app binary. The only safe place for this token is inside a server-side function that your FlutterFlow app calls. This is the core security principle for this entire integration.
Pro tip: Monday.com also supports OAuth 2.0 for multi-tenant apps where different users log in with their own monday accounts. For a single-account use case (your own data in your app), the personal API token is the right choice.
Expected result: You have a monday.com API v2 token copied and ready to use in the next step.
Deploy a Firebase Cloud Function to proxy GraphQL requests
Open the Firebase Console (console.firebase.google.com) and select or create a Firebase project. In the left sidebar, click Functions, then click Get Started and follow the prompts to enable Cloud Functions if you have not already. You will need to upgrade to the Blaze (pay-as-you-go) plan to deploy functions — the free tier includes a generous allocation that covers testing and low-traffic apps. Create a new Cloud Function. In the Functions dashboard, click Create Function. Name it `mondayProxy`. Set the trigger to HTTP — this gives it a public URL that FlutterFlow will call. In the function's source code editor, paste the Node.js code shown below. In the Runtime environment variables section, add a variable named `MONDAY_TOKEN` with the value of your API v2 token from Step 1. This keeps the token out of your code and out of your app. Deploy the function. After a minute or two, Firebase will give you a function URL that looks like `https://us-central1-your-project.cloudfunctions.net/mondayProxy`. Copy this URL — it is what you will configure in your FlutterFlow API Call in the next step. The function receives a GraphQL query string from FlutterFlow, forwards it to monday.com with the Authorization header, and returns the parsed board/item data as clean JSON.
1const functions = require('firebase-functions');2const fetch = require('node-fetch');34exports.mondayProxy = functions.https.onRequest(async (req, res) => {5 res.set('Access-Control-Allow-Origin', '*');6 res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');7 res.set('Access-Control-Allow-Headers', 'Content-Type');89 if (req.method === 'OPTIONS') {10 res.status(204).send('');11 return;12 }1314 const { query, variables } = req.body;15 const token = process.env.MONDAY_TOKEN;1617 try {18 const response = await fetch('https://api.monday.com/v2', {19 method: 'POST',20 headers: {21 'Content-Type': 'application/json',22 'Authorization': token,23 'API-Version': '2024-01'24 },25 body: JSON.stringify({ query, variables: variables || {} })26 });2728 const data = await response.json();2930 // Flatten column_values to key-value pairs for easy FF binding31 if (data.data && data.data.boards) {32 data.data.boards = data.data.boards.map(board => ({33 ...board,34 items_page: {35 ...board.items_page,36 items: (board.items_page?.items || []).map(item => ({37 id: item.id,38 name: item.name,39 columns: Object.fromEntries(40 (item.column_values || []).map(cv => [cv.id, cv.text || cv.value || ''])41 )42 }))43 }44 }));45 }4647 res.json(data);48 } catch (err) {49 res.status(500).json({ error: err.message });50 }51});Pro tip: Add `node-fetch` to your package.json dependencies in the functions directory: `"node-fetch": "^2.7.0"`. The Firebase inline editor handles this automatically if you add it to the dependencies field.
Expected result: The Firebase Function is deployed and you have its public HTTPS URL. Calling it with a test GraphQL query body returns board JSON from monday.com.
Create one FlutterFlow API Call that POSTs a GraphQL query
In your FlutterFlow project, click API Calls in the left navigation panel. Click + Add and select Create API Group. Name the group 'Monday' and set the Base URL to your Cloud Function URL from Step 2 (for example, `https://us-central1-your-project.cloudfunctions.net/mondayProxy`). Because the token lives in the Cloud Function environment, there are no shared headers to configure in FlutterFlow — the API Group needs no Authorization header. Inside the API Group, click + Add Call to create the first API Call. Name it 'GetBoards'. Set the method to POST. Leave the endpoint path empty — all requests go to the function's root URL. Switch to the Body tab and set the body type to JSON. In the JSON body field, add the GraphQL query as a variable: set `query` to a variable named `graphqlQuery` using the `{{ variable }}` syntax, and `variables` to an optional object. Switch to the Variables tab and add a variable named `graphqlQuery` of type String with the default value being your board-fetch query (see the code block below). This means you can change what data you fetch by changing the query string sent from the app, without creating a new API Call for each data shape you need — one Call covers boards, items, column values, and mutations. Click Test. In the test panel, paste your boards query into the `graphqlQuery` field, then click Run Test. If the function is deployed correctly, you should see a JSON response with your board data nested under `data.boards`. If you see a 401 error, the function's `MONDAY_TOKEN` environment variable is missing or incorrect — go back to Firebase Console and check the Runtime environment variables.
1{2 "query": "{{ graphqlQuery }}",3 "variables": {}4}56// Example graphqlQuery value for fetching boards + items + column values7// (paste this as the variable value in the FlutterFlow test panel):8// query {9// boards(limit: 10) {10// id11// name12// items_page(limit: 50) {13// items {14// id15// name16// column_values {17// id18// text19// value20// }21// }22// }23// }24// }Pro tip: Batch your board + items + column reads into one nested GraphQL query rather than separate API Calls. This uses one of your ~60 requests per minute instead of three, and returns all the data you need in a single response.
Expected result: The 'GetBoards' API Call tests successfully and returns a JSON response. You can see your monday.com board names and item data in the test response panel.
Map response JSON to a Custom Data Type and bind to a ListView
In your FlutterFlow project, click the Data Types section in the left navigation panel. Click + Add Data Type. Name it 'MondayItem' and add the following fields: `id` (String), `name` (String), and any column values you want to display — for example, `status` (String), `dueDate` (String), and `assignee` (String). The proxy function in Step 2 flattens the nested `column_values` array into a simple key-value object, so you can reference your columns by their ID using a JSON path like `$.data.boards[0].items_page.items[*].columns.status__1` (replace `status__1` with your actual column ID from the monday.com board). Now navigate to the screen where you want to show the board data. In the Action Flow Editor, add an action on the screen's On Load event: select Backend/API Call → Monday → GetBoards. Set the `graphqlQuery` variable to the board query string. After the API Call completes, store the response in an App State variable of type List (using your MondayItem Custom Data Type). Add a ListView widget to the screen. In the ListView's generate children from variable setting, select your App State list. Inside the list item template, add a Card widget with a Text widget bound to `item.name`, another Text widget for the status column, and conditional color on the status text (green for Done, orange for Working on it, red for Stuck) using conditional visibility or a custom function. To create or update items, add a new API Call in the same API Group named 'CreateItem'. The proxy accepts the same GraphQL mutation syntax — simply change the query variable to a mutation string instead of a query string. For example, a mutation to create an item takes the board ID, group ID, and item name as variables passed in the `variables` object.
1// JSON Paths for binding (after proxy flattening):2// Board names: $.data.boards[*].name3// Board IDs: $.data.boards[*].id4// Item names: $.data.boards[0].items_page.items[*].name5// Item IDs: $.data.boards[0].items_page.items[*].id6// Status column: $.data.boards[0].items_page.items[*].columns.status__17// Date column: $.data.boards[0].items_page.items[*].columns.date489// Mutation example (to create an item):10// mutation {11// create_item (12// board_id: 1234567890,13// group_id: "topics",14// item_name: "New task from FlutterFlow"15// ) {16// id17// }18// }Pro tip: To find your column IDs, open monday.com, click on a board, click the three-dot menu on a column header, and select 'Column settings'. The column ID (like `status__1` or `date4`) appears in the URL or settings panel.
Expected result: Your FlutterFlow screen shows a ListView populated with real monday.com board items. Each card displays the item name and status column value pulled from monday.com via the Cloud Function proxy.
Add error handling for rate limits and complexity errors
Monday.com's GraphQL API has two types of limits you need to handle gracefully: the request rate limit (approximately 60 requests per minute on most plans) and the complexity budget (a per-query cost based on how many nested objects are fetched). If you exceed either limit, the API returns a 429 status code or a JSON error object with a `error_code` of `ComplexityException`. In your FlutterFlow action flow, after each Monday API Call, add a conditional check on the API Call response's HTTP status code. If the status is 429 or if the JSON response body contains `errors[0].extensions.code` equal to `ComplexityException`, show a user-facing message ('Board data is loading, please wait a moment') and schedule a retry after a short delay using FlutterFlow's Wait action (set to 3-5 seconds). To avoid hitting the complexity budget in the first place, keep your GraphQL query focused: only fetch the columns you actually display, limit item pages to 50 items at a time, and avoid nesting more than two levels deep (boards → items → column_values is fine; adding subitems or updates to the same query increases complexity). Use pagination for large boards: monday.com's `items_page` supports a `cursor` field for fetching the next page of items — add a 'Load More' button to your ListView that sends the next cursor value as a variable in a follow-up API Call. Add a pull-to-refresh gesture to the ListView (FlutterFlow has a built-in Refresh Indicator widget) so users can manually refresh board data without the app automatically polling. Automatic polling every few seconds will quickly exhaust your rate limit on busier boards.
1// Paginated items query using cursor:2// query GetItems($boardId: ID!, $cursor: String) {3// boards(ids: [$boardId]) {4// items_page(limit: 50, cursor: $cursor) {5// cursor6// items {7// id8// name9// column_values { id text }10// }11// }12// }13// }Pro tip: If you're building a read-only dashboard that doesn't need real-time data, fetch board data once on screen load and cache it in App State. Only re-fetch on pull-to-refresh to avoid hitting rate limits.
Expected result: Your app handles rate limit errors gracefully with a user-visible message and retry logic. The ListView supports pagination for large boards via a 'Load More' button and pull-to-refresh.
Common use cases
Project status dashboard app showing live board data
A team builds a FlutterFlow mobile app that displays their monday.com project boards as a native list of cards, each showing the board name, number of items, and status column values. Team members check project health on their phones without opening a browser.
Build a 'Project Dashboard' screen that shows all our monday.com boards as cards with item counts and a color-coded status indicator for each board.
Copy this prompt to try it in FlutterFlow
Field service app that creates and updates work order items
A field service company builds a FlutterFlow app for technicians that reads open work orders from a monday.com board and lets them update the status column when a job is completed. The app uses GraphQL mutations through the proxy to write changes back to monday.com.
Create a screen listing open work orders from our monday.com 'Field Jobs' board, with a button on each card to mark the job as 'Done' — that should update the Status column in monday.com.
Copy this prompt to try it in FlutterFlow
Client portal showing approved deliverables from a shared board
An agency builds a FlutterFlow app for clients that shows only items from the client's monday.com board where the status column value is 'Approved'. Clients see a clean native view of approved deliverables without needing a monday.com account.
Show our clients a list of approved deliverables from their monday.com board — only show items where Status equals 'Approved', displayed as cards with the item name and due date.
Copy this prompt to try it in FlutterFlow
Troubleshooting
API Call returns 200 but the response body contains `{"errors": [{"message": "Not authenticated"}]}`
Cause: The Cloud Function is not sending the `Authorization` header to monday.com, or the `MONDAY_TOKEN` environment variable is empty or incorrect in the Firebase Function configuration.
Solution: Go to the Firebase Console → Functions → mondayProxy → Runtime environment variables. Confirm that `MONDAY_TOKEN` is set to your monday.com API v2 token. Re-deploy the function after correcting the variable. Test by calling the function URL directly from a browser with a simple GraphQL query body.
ListView shows empty or the JSON path binding returns no data
Cause: The `column_values` array was not flattened in the proxy, so the raw nested JSON structure doesn't match the JSON path you configured in FlutterFlow's response binding.
Solution: Confirm your Cloud Function's flattening logic is working correctly — test the function URL directly and check that the response contains a `columns` object with key-value pairs instead of a raw `column_values` array. Update the JSON paths in FlutterFlow's API Call response settings to match the actual response structure.
API Call works in FlutterFlow Test Mode (web) but fails on iOS/Android with a network error
Cause: The Firebase Cloud Function URL may need CORS headers for web targets (already included in the example function above), but a network error on mobile typically indicates the function URL is wrong or the function failed to deploy correctly.
Solution: Double-check the function URL in your FlutterFlow API Group's Base URL field. Open the Firebase Console and click the function to see recent invocation logs — error messages there will show the root cause. Re-deploy if the function is in an error state.
GraphQL mutation returns `{"error_code": "ItemsLimitationException"}` or `board not found`
Cause: The board ID or group ID in the mutation query is incorrect, or the account associated with the token does not have write access to that board.
Solution: Open monday.com in the browser, navigate to the board, and copy the board ID from the URL (it is the number in the URL after `/boards/`). Confirm the group ID by clicking the group header → Settings. Check that the monday.com account tied to the API token is a member of the board with edit permissions.
Best practices
- Never put the monday.com API token in a FlutterFlow API Call header — it grants full account access and must live only in a server-side Cloud Function environment variable.
- Use a single FlutterFlow API Call with a variable `graphqlQuery` field instead of multiple Calls per endpoint — monday.com's GraphQL model makes one parameterized Call more maintainable than a separate Call per query type.
- Batch board, item, and column reads into one nested GraphQL query to minimize API Call count and stay well within the ~60 requests per minute rate limit.
- Flatten nested `column_values` JSON in your Cloud Function proxy so FlutterFlow's JSON-path binding stays simple and readable — avoid trying to bind deeply nested arrays directly in FlutterFlow.
- Always use pagination (`items_page` with `cursor`) for boards with more than 50 items — fetching unlimited items in a single query will hit the complexity budget and return a ComplexityException error.
- Add pull-to-refresh on board screens rather than automatic polling — polling at short intervals will exhaust your rate limit, especially if multiple users are running the app simultaneously.
- Use `shippo_test_` tokens during development — similarly, monday.com supports sandbox environments; test mutations carefully before running them against production board data.
- Add explicit error handling for `ComplexityException` and 429 responses in your FlutterFlow action flow so users see a clear message instead of a blank screen when limits are reached.
Alternatives
Notion uses a simpler REST API with per-resource paths and a straightforward Bearer token — easier to set up if you don't need monday.com's GraphQL mutation capabilities.
Airtable has a REST API with a more intuitive table/record structure than monday.com's GraphQL model, making it easier to bind flat record data directly in FlutterFlow.
Asana's REST API is simpler for task-and-project use cases where you don't need monday.com's custom column types or automations.
Frequently asked questions
Why does monday.com use GraphQL instead of REST?
Monday.com's API is designed to let you fetch exactly the data you need in a single request by describing your query in GraphQL syntax — boards, items, column values, and user details can all be fetched together with one POST. This reduces the number of API calls you need, but it also means the FlutterFlow setup is different from standard REST integrations: there is one endpoint, and the 'path' is a query string in the POST body.
Can I connect FlutterFlow to monday.com without a Cloud Function?
Only for read-only use cases with public boards and truly low-risk tokens. In practice, any monday.com API token grants access to your entire account, so placing it in a FlutterFlow API Call is genuinely unsafe — it ships in the app bundle. The Cloud Function proxy is the correct approach for any production app. The setup takes about 30 minutes and is a one-time effort.
How do I find my monday.com board ID and column IDs for the GraphQL query?
The board ID is in the URL when you open the board in monday.com: `monday.com/boards/1234567890` — the number after `/boards/` is the board ID. Column IDs appear when you click a column's three-dot menu → Column Settings, or you can run a test GraphQL query `{ boards(limit:1) { columns { id title } } }` in the FlutterFlow API Call test panel to list all column IDs for a board.
Can I create and update monday.com items from FlutterFlow?
Yes. GraphQL mutations like `create_item`, `change_column_value`, and `change_simple_column_value` work through the same proxied API Call as read queries — just set the query variable to a mutation string instead. The same Cloud Function handles both queries and mutations, and the same ~60 req/min rate limit applies.
What monday.com plan do I need for API access?
Monday.com provides API access on paid Work OS plans. The API is not available on the free plan. Check monday.com's current pricing page to confirm API access for your specific tier, as plan features can change. The free trial typically includes API access for testing.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation