Integrating Monday.com with Lovable requires Edge Functions to proxy Monday.com's GraphQL API. Store your Monday.com API token in Cloud Secrets, create an Edge Function that sends GraphQL queries and mutations, and build custom board views in Lovable's React frontend. Monday.com uses a board-column-item paradigm and GraphQL for all data operations — you construct queries to fetch boards, items, and column values, and mutations to create or update items.
Why integrate Monday.com with Lovable?
Monday.com is one of the most widely adopted work management platforms, used by over 225,000 organizations for everything from project tracking to CRM, sales pipelines, HR onboarding, and operations management. Its board-column-item model is intuitive for non-technical teams, which is precisely why so many businesses use it as their central hub — and why developers are often asked to build custom integrations that connect Monday.com data to other systems.
Building a custom Lovable app on top of Monday.com data opens up scenarios that Monday.com's own interface doesn't support: client-facing portals where external stakeholders see selected board data without needing a Monday.com license, automated intake forms that create Monday.com items from customer submissions, custom reporting dashboards that combine Monday.com data with data from other sources, and approval workflows with custom UI logic beyond Monday.com's built-in automations.
Monday.com's API is GraphQL-based, which differs from the REST APIs used by Jira, ClickUp, and most other tools covered in these tutorials. GraphQL lets you specify exactly which fields you want in a single query, reducing over-fetching significantly. The tradeoff is that you need to construct GraphQL query strings rather than simply calling URL endpoints — this tutorial provides ready-to-use query templates for the most common operations.
Integration method
Monday.com has no native Lovable connector. All Monday.com API calls use the GraphQL API at api.monday.com/v2, proxied through Supabase Edge Functions. The API token is stored in Cloud Secrets and included as an Authorization header. Edge Functions send GraphQL queries to fetch board structures and items, and mutations to create, update, and delete items, then return the results to the Lovable frontend.
Prerequisites
- A Lovable project with Cloud enabled
- A Monday.com account with at least one board
- A Monday.com API token — generate at monday.com under your profile picture → Developers → My Access Tokens
- The board IDs you want to integrate with (found in the URL when viewing a board: monday.com/boards/XXXXXXX)
- Basic understanding of GraphQL concepts (queries, mutations, variables) — optional but helpful
Step-by-step guide
Generate a Monday.com API token
Generate a Monday.com API token
Monday.com uses personal API tokens for programmatic authentication. Log in to your Monday.com account and click your profile picture in the bottom-left corner. In the menu, click 'Developers' (or navigate to the API section). On the developer page, click 'My Access Tokens' and then 'Show' to reveal your API token. Copy this token — it's a long alphanumeric string. Monday.com also offers OAuth2 for apps that need other users to connect their accounts (the 'Monday Apps' OAuth flow). For a personal integration or an internal tool where you're the only Monday.com account involved, the personal API token is simpler and sufficient. For a SaaS app where each customer connects their own Monday.com workspace, you'll need to build the OAuth2 flow — but that's beyond the scope of this tutorial. Note your board IDs as well. When viewing a board in Monday.com, the URL shows the board ID: monday.com/boards/{boardId}/... Copy the numeric board IDs for the boards you want to integrate with. You can also discover board IDs programmatically by querying the Monday.com API — the Edge Function in Step 3 includes a get-boards action for this. Monday.com's API rate limits are 10,000 complexity points per minute. Each query costs complexity based on how many records and fields you fetch — a simple item list with 3 fields costs roughly 50 complexity points, meaning you can make about 200 such queries per minute. This is more than enough for most custom dashboard use cases.
Pro tip: Monday.com tokens are scoped to your user account and have access to all boards your account can see. There is no way to restrict a token to specific boards — if you're concerned about scope, create a dedicated Monday.com account with limited board access to use for the integration.
Expected result: You have your Monday.com personal API token copied. You have the board IDs for the boards you want to integrate with.
Store Monday.com credentials in Cloud Secrets
Store Monday.com credentials in Cloud Secrets
Open your Lovable project, click '+' in the top-right area to access panels, select 'Cloud', and expand the Secrets section. Add MONDAY_API_TOKEN with your personal API token value. This is the only credential required — Monday.com's API is a single endpoint (https://api.monday.com/v2) that uses the token for both authentication and authorization. You can optionally add MONDAY_BOARD_IDS as a comma-separated list of board IDs you commonly access (e.g., '1234567,7654321,9876543'). This makes it easy to list available boards in your frontend without hardcoding IDs, though it's not required — board IDs can also be passed as parameters in each Edge Function call. The Monday.com API token provides access to all boards, workspaces, and items your Monday.com user account can see. Unlike Jira (where you have an email and token pair), Monday.com uses just the token in an Authorization header. All authenticated API calls to api.monday.com/v2 include the header 'Authorization: {token}' (no 'Bearer' prefix — Monday.com uses a non-standard authorization format). Monday.com's API endpoint is the same for all operations: POST https://api.monday.com/v2 with a JSON body containing the GraphQL query string and optional variables object. This single-endpoint design is typical of GraphQL APIs.
Pro tip: Monday.com uses 'Authorization: {token}' (without 'Bearer') unlike most other APIs. Using 'Bearer {token}' format returns a 401 Unauthorized error even with a valid token — this is a common integration stumbling point.
Expected result: MONDAY_API_TOKEN is set in Cloud Secrets. No credentials appear in any source code file.
Create the Monday.com GraphQL Edge Function
Create the Monday.com GraphQL Edge Function
The core of the Monday.com integration is a single Edge Function that accepts a GraphQL query and variables and proxies them to Monday.com's API. Because all Monday.com operations — reading boards, fetching items, creating items, updating columns — go through the same POST endpoint, one Edge Function handles everything. GraphQL queries for Monday.com follow the platform's schema. The key entities are: boards (workspaces and their column definitions), items (individual rows on a board), column_values (the data in each cell), groups (sections within a board), and updates (comments/activity on items). For a custom dashboard, the most useful queries fetch: the board's columns structure (to know which column names map to which IDs), all items on a board with their column values, and filtered subsets using the limit and page parameters for pagination. For creating items, you use the create_item mutation with the board_id, group_id, item_name, and a column_values object mapping column IDs to their values. Column values in Monday.com use different formats depending on the column type: a Status column value is a JSON object like {label: 'Done'}, a Date column is {date: '2026-03-15'}, a People column is {personsAndTeams: [{id: 123, kind: 'person'}]}, and a Text column is just the string value. Your Edge Function should accept pre-formatted column values from the frontend rather than trying to format them internally.
Create a Supabase Edge Function at supabase/functions/monday/index.ts. It should read MONDAY_API_TOKEN from Deno.env. Accept a POST body with: query (GraphQL query string) and variables (optional object). POST both to https://api.monday.com/v2 with the Authorization header set to the token (no 'Bearer' prefix), Content-Type application/json, and API-Version '2024-01' header. Return the data or errors from the GraphQL response. Add CORS headers. Also add a convenience action='get-boards' that automatically runs a board listing query, and action='get-board-items' that takes a boardId and returns items with all column values.
Paste this in Lovable chat
1// supabase/functions/monday/index.ts2import { serve } from "https://deno.land/std@0.168.0/http/server.ts";34const MONDAY_TOKEN = Deno.env.get("MONDAY_API_TOKEN") ?? "";5const MONDAY_URL = "https://api.monday.com/v2";6const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization" };78async function callMonday(query: string, variables?: Record<string, unknown>) {9 const res = await fetch(MONDAY_URL, {10 method: "POST",11 headers: {12 Authorization: MONDAY_TOKEN,13 "Content-Type": "application/json",14 "API-Version": "2024-01",15 },16 body: JSON.stringify({ query, variables }),17 });18 return res.json();19}2021serve(async (req) => {22 if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders });23 try {24 const body = await req.json();25 const { action, query, variables, boardId, itemName, columnValues, groupId } = body;2627 let result;28 if (action === "get-boards") {29 result = await callMonday(`query { boards(limit: 50) { id name description state } }`);30 } else if (action === "get-board-items") {31 result = await callMonday(32 `query ($boardId: ID!) {33 boards(ids: [$boardId]) {34 name35 columns { id title type }36 items_page(limit: 100) {37 items {38 id name39 column_values { id text value }40 group { title }41 }42 }43 }44 }`,45 { boardId: String(boardId) }46 );47 } else if (action === "create-item") {48 result = await callMonday(49 `mutation ($boardId: ID!, $groupId: String!, $name: String!, $cols: JSON!) {50 create_item(board_id: $boardId, group_id: $groupId, item_name: $name, column_values: $cols) {51 id name52 }53 }`,54 { boardId: String(boardId), groupId: groupId ?? "topics", name: itemName, cols: JSON.stringify(columnValues ?? {}) }55 );56 } else if (query) {57 result = await callMonday(query, variables);58 } else {59 return new Response(JSON.stringify({ error: "Provide action or query" }), { status: 400, headers: corsHeaders });60 }6162 return new Response(JSON.stringify(result), { headers: { ...corsHeaders, "Content-Type": "application/json" } });63 } catch (err) {64 return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } });65 }66});Pro tip: Monday.com's API-Version header is required starting from 2024. Use '2024-01' as the version string. Without this header, you may get deprecation warnings or unexpected behavior as Monday.com migrates between API versions.
Expected result: The monday Edge Function is deployed. Calling it with action='get-boards' returns a list of your Monday.com boards with their IDs and names. Calling action='get-board-items' with a boardId returns all items with their column values.
Build a Monday.com board view in Lovable
Build a Monday.com board view in Lovable
With the Edge Function in place, build a custom board visualization. Ask Lovable to create a page that loads a Monday.com board, displays items in a column-grouped layout, and provides basic CRUD operations. The data structure returned by the get-board-items action contains the board's column definitions and all items with their column_values. Each column_value has an id (matching the board's column definitions), a text field (the human-readable value), and a value field (the raw JSON value used for creating/updating items). Use the text field for display and the id for targeting specific columns in mutations. For a Kanban view, group items by the Status column value. Fetch the column definitions first to find which column has type 'color' (Status columns are type 'color' in Monday.com) and use that column's text values as your Kanban columns. Items with a Status text of 'Done' go in the Done column, 'In Progress' in Working On It, and so on. For item creation from your Lovable form, you need the group_id within the board (groups are the colored sections like 'This Week', 'Next Week'). Fetch groups with the board query and display a dropdown to let users select which group to add to. The column_values object for creation uses column IDs as keys — map your form fields to the correct column IDs from the board's column definitions.
Build a Monday.com board view at /board. On load, call the monday Edge Function with action='get-board-items' and boardId=YOUR_BOARD_ID. Display items in a Kanban board grouped by their Status column value. Each item card shows: name, due date (if present), and assignee name. Add a Create Item button that opens a form with name, status, and due date. On submit, call monday with action='create-item' with the board ID, default group ID, item name, and column values object with status and date. Refresh the board after creation.
Paste this in Lovable chat
Pro tip: Monday.com column IDs are typically human-readable strings like 'status', 'date', 'person', or 'text'. However, duplicate columns or renamed columns may have IDs like 'status_1', 'date4', etc. Always fetch the board's column definitions first to see the actual IDs for your specific board rather than assuming they match the column titles.
Expected result: The board view displays Monday.com items grouped into Kanban columns by status. Item cards show name, date, and assignee. Creating an item from the form adds it to Monday.com and it appears in the board.
Handle Monday.com webhooks for real-time updates
Handle Monday.com webhooks for real-time updates
Monday.com webhooks notify your app when items are created, updated, or when column values change. This enables your Lovable app to stay in sync with Monday.com without polling the API on a timer. To set up webhooks, create a webhook receiver Edge Function in Lovable. Monday.com webhooks use a challenge-response verification: when you register the webhook, Monday.com sends a POST request with a challenge field, and your endpoint must respond with the same challenge value. After this verification handshake, Monday.com sends event payloads to your endpoint. Monday.com webhooks are created through the API (POST mutation to subscribe_webhook) or through the Monday.com UI under Automations. The webhook payload includes: event.type (create_item, change_column_value, etc.), event.boardId, event.itemId, and event.columnId for column change events. For a client project portal, listen for status column changes — when a task is marked Done in Monday.com by your team, the webhook triggers and updates the client's view in Supabase, removing the need for the client portal to poll the API continuously.
Create a Supabase Edge Function at supabase/functions/monday-webhook/index.ts. It should: 1) handle Monday.com's challenge verification by checking if the request body has a 'challenge' field and responding with {challenge: value} immediately, 2) on change_column_value events for status columns, update the corresponding item in the Supabase monday_items table (columns: item_id, board_id, name, status, updated_at) using service role key, 3) on create_item events, insert a new row into monday_items with the item details.
Paste this in Lovable chat
1// supabase/functions/monday-webhook/index.ts2import { serve } from "https://deno.land/std@0.168.0/http/server.ts";3import { createClient } from "https://esm.sh/@supabase/supabase-js@2";45serve(async (req) => {6 try {7 const body = await req.json();89 // Handle Monday.com webhook verification challenge10 if (body.challenge) {11 return new Response(JSON.stringify({ challenge: body.challenge }), {12 headers: { "Content-Type": "application/json" },13 });14 }1516 const supabase = createClient(17 Deno.env.get("SUPABASE_URL") ?? "",18 Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? ""19 );2021 const event = body.event;22 if (event?.type === "change_column_value" && event?.columnId?.includes("status")) {23 await supabase.from("monday_items").upsert({24 item_id: String(event.itemId),25 board_id: String(event.boardId),26 status: event.value?.label?.text ?? "",27 updated_at: new Date().toISOString(),28 }, { onConflict: "item_id" });29 }3031 if (event?.type === "create_item") {32 await supabase.from("monday_items").insert({33 item_id: String(event.itemId),34 board_id: String(event.boardId),35 name: event.itemName ?? "",36 status: "New",37 updated_at: new Date().toISOString(),38 });39 }4041 return new Response("OK", { status: 200 });42 } catch (err) {43 return new Response(err.message, { status: 500 });44 }45});Pro tip: Register Monday.com webhooks through the API or Automations UI. The webhook URL is your Edge Function URL. Monday.com requires HTTPS — your deployed Lovable project's Supabase function URL is always HTTPS, so this works without any extra configuration.
Expected result: The webhook Edge Function handles Monday.com's challenge verification. When a status changes in Monday.com, the webhook fires and the Supabase monday_items table is updated within seconds.
Common use cases
Client project portal with selected board data
An agency managing client projects in Monday.com wants to give each client a branded portal showing only their project's board data without requiring them to have a Monday.com license. The Edge Function fetches items from the client's specific board filtered by their company label, and the frontend shows a clean status timeline with task names, assignees, due dates, and completion status.
Build a client portal at /project-status. The Edge Function should query Monday.com's GraphQL API for board ID 1234567 and return all items with their name, status column value, due date column value, and owner column value. Display items grouped by status in a timeline view. Filter to show only items where the client_name column equals 'Acme Corp'. Add a last-updated timestamp to each item.
Copy this prompt to try it in Lovable
Lead intake form that creates Monday.com CRM items
A sales team manages their CRM in Monday.com and wants a custom lead capture form on their Lovable website. When a potential customer submits the form, an Edge Function creates a new item in the Monday.com CRM board with the lead's name, company, email, and interest level, and assigns it to the next available sales rep in a round-robin column.
Add a lead capture form to the /contact page with fields: name, company, email, phone, product_interest (dropdown), and message. On submit, call an Edge Function that creates a new item in Monday.com board ID 7654321 with: the name as the item name, and column values for email, phone, company, product_interest, and status set to 'New Lead'. Save the Monday.com item ID to the Supabase leads table and show a success confirmation.
Copy this prompt to try it in Lovable
Automated task creation from app events
A SaaS app wants to automatically create Monday.com tasks when specific events occur — for example, when a new user signs up, create an onboarding checklist item assigned to the customer success team, or when a payment fails, create a follow-up task for the support team. Edge Functions listen for Supabase database events and create Monday.com items in response.
When a new user is created in the Supabase auth.users table, call a Monday.com Edge Function that creates an item in the onboarding board (ID 9876543) with: item name = user's email, status = 'New Signup', email column = user email, signup_date column = today's date. Assign it to the customer success person with the least items currently assigned in that board.
Copy this prompt to try it in Lovable
Troubleshooting
Monday.com API returns 401 Unauthorized with a valid-looking token
Cause: Monday.com uses 'Authorization: {token}' without the 'Bearer' prefix. Using 'Bearer {token}' or 'Basic {token}' formats are both invalid for Monday.com's API and return 401 even with a correct token value.
Solution: Set the Authorization header to exactly the token string with no prefix: headers: { Authorization: MONDAY_TOKEN }. Do not prepend 'Bearer' or 'Basic'. This is a non-standard pattern that catches many developers off guard.
1// Correct Monday.com auth header (no 'Bearer' prefix)2const headers = {3 Authorization: Deno.env.get("MONDAY_API_TOKEN") ?? "", // No 'Bearer'4 "Content-Type": "application/json",5 "API-Version": "2024-01",6};GraphQL query returns errors array with 'Field X doesn't exist on type Y'
Cause: The GraphQL schema has changed between API versions, or the query is using a deprecated field name. Monday.com updated its API in 2024 — older examples online may use field names like 'items' (deprecated) instead of 'items_page'.
Solution: Use the Monday.com API Explorer at developer.monday.com/api-reference to validate queries against the current schema. Ensure you're including the API-Version: 2024-01 header and use items_page instead of the older items field for fetching board items.
1// Use items_page (current) not items (deprecated)2const query = `query {3 boards(ids: [${boardId}]) {4 items_page(limit: 100) { // NOT: items { ... }5 items { id name column_values { id text value } }6 }7 }8}`;create_item mutation fails with 'column value type mismatch'
Cause: The column_values JSON object is using the wrong format for a specific column type. Each Monday.com column type (Status, Date, People, Email, etc.) expects a different JSON structure for creating/updating values.
Solution: Check Monday.com's column types documentation at developer.monday.com for the exact JSON format each column type expects. The most common formats are: Status column: JSON.stringify({label: 'Done'}), Date column: JSON.stringify({date: '2026-03-15'}), Text column: 'plain string value', Email column: JSON.stringify({email: 'user@example.com', text: 'user@example.com'}).
1// Example column values for different types2const columnValues = {3 status: JSON.stringify({ label: "In Progress" }),4 date4: JSON.stringify({ date: "2026-04-01" }),5 text: "plain text value",6 email: JSON.stringify({ email: "user@example.com", text: "user@example.com" }),7};Webhook challenge verification fails and webhooks don't register
Cause: Monday.com's webhook registration sends a challenge POST to your endpoint and expects {challenge: 'value'} back within a few seconds. If your Edge Function is slow to cold-start or returns the challenge in the wrong format, registration fails silently.
Solution: Ensure the challenge response is the first check in your webhook handler, runs before any other logic, and returns the challenge wrapped in a JSON object with Content-Type: application/json. Test by registering the webhook in Monday.com's Automations section and checking Cloud Logs for the incoming challenge request.
1// Handle challenge immediately — before any other processing2if (body.challenge) {3 return new Response(JSON.stringify({ challenge: body.challenge }), {4 headers: { "Content-Type": "application/json" },5 });6}Best practices
- Use Monday.com's API Explorer at developer.monday.com/api-reference to test GraphQL queries before writing Edge Function code — it shows real data from your account and validates queries against the current schema.
- Always include the API-Version: 2024-01 header to pin to the current stable schema and avoid deprecated field behavior.
- Fetch a board's column definitions once and cache them, rather than re-fetching on every items request. Column structures rarely change and the definition query adds unnecessary complexity to item fetches.
- Store Monday.com item IDs in Supabase when you create items via the API, so you can later update or link to them. The item ID returned by create_item is the permanent identifier for that row.
- Use Monday.com webhooks for real-time sync rather than polling the API on a timer. Webhooks are more efficient and avoid rate limit consumption for detecting changes.
- Handle Monday.com's complexity-based rate limiting — each query consumes complexity points based on the fields and number of records returned. Request only the fields you display to minimize complexity costs.
- For multi-workspace Monday.com apps where different customers connect their own accounts, build the OAuth2 flow using Monday.com's app authentication rather than personal API tokens.
Alternatives
Jira is better for software engineering teams needing sprint management, burndown charts, and complex issue relationships; Monday.com suits broader cross-functional teams with a visual board approach.
ClickUp has a similar board paradigm to Monday.com but with a deeper task hierarchy and REST API that may be easier to work with for developers than Monday.com's GraphQL.
Linear is a native Lovable connector focused on engineering teams, with much simpler integration setup than Monday.com's manual GraphQL Edge Function approach.
Frequently asked questions
Why does Monday.com use GraphQL instead of REST?
Monday.com chose GraphQL because its flexible board model — where boards can have different column types, groups, and subitem structures — benefits from being able to request exactly the data shape needed for each use case. With REST, you'd need many different endpoints for different data combinations. With GraphQL, a single endpoint handles all queries and you specify exactly which fields to return, reducing over-fetching significantly.
How do I get a board's column IDs to use in create_item mutations?
Fetch the board's column definitions using the columns field in your GraphQL query: boards(ids: [$boardId]) { columns { id title type } }. The id values returned are what you use as keys in the column_values object when creating or updating items. Column IDs are stable identifiers assigned by Monday.com — they don't change when you rename a column, but they may be things like 'status', 'date4', or 'person_1' depending on how the board was set up.
Can I create subitems using the Monday.com API?
Yes. Monday.com supports subitems through the create_subitem mutation, which takes a parent_item_id and column values similar to create_item. Subitems have their own board structure separate from the parent board, and you can fetch subitem column definitions through the subitems_page query. The subitem hierarchy is one level deep — you cannot nest subitems under other subitems.
How do I handle different account tiers? Some Monday.com plans have API limitations.
Monday.com's API access varies by plan. The Free plan has limited API access. Pro and Enterprise plans have full API access including webhooks. Complexity limits also vary by plan. If your app targets other companies' Monday.com accounts (not just your own), ensure your users are on a plan that supports API access. The Monday.com API returns a clear error when plan limitations are exceeded.
Can multiple Lovable users connect their own Monday.com accounts?
Yes, but this requires implementing Monday.com's OAuth2 app authentication flow rather than personal API tokens. You create a Monday.com app in their developer portal, implement the OAuth2 redirect flow where each user authorizes your app, store their access token in Supabase, and use their token in Edge Function calls. This is the multi-tenant pattern and is significantly more complex than the single-token approach in this tutorial.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation