
Connect Forms to monday.com: Lead Form → Item via GraphQL API
TL;DR: „monday WorkForms is fine – but if you want a custom-designed form AND monday.com data, build it in Lovable and write straight into the board via GraphQL."
— Till Freitag📌 Lovable Forms Series · Part 4 of 6
You've seen the SaaS options in Part 1, built it yourself in Part 2, and made it production-ready in Part 3. Now we wire the backend: your Lovable form lands as an item in monday.com.
Connect Forms to monday.com: Lead Form → Item via GraphQL API
You use monday.com as your CRM, but you don't want to embed a WorkForm – you want your own Lovable form, pixel-perfect on brand, while the lead still ends up in the right board. That's what the monday GraphQL API is for.
In this part we show the full pipeline: from form submit, through an Edge Function, to a freshly created item in your monday board – including field mapping, auth strategy, and the classic gotchas.
Why not just WorkForms?
monday WorkForms (see Part 1) is the fastest option – but it has limits:
- Design is bound to monday templates
- Embedding changes your cookie/GDPR setup
- Conditional logic is okay, but not as free as your own code
- Multi-step flows with custom progress bar? Limited
The moment your form is part of your brand experience (pricing wizard, onboarding, demo request), you'll want to build it yourself – while the lead data still lives where your sales team works: monday CRM.
Architecture in 3 boxes
[Lovable Form] → [Edge Function] → [monday GraphQL API] → [Board Item]
(frontend) (auth + map) (api.monday.com) (CRM)Critical: never keep your monday API token in the frontend. It belongs in an Edge Function – as a secret in Lovable Cloud or Supabase.
Step 1: Create a monday API token
- In monday.com: Avatar → Developers → My Access Tokens
- Copy the token (Personal Token is fine for testing; for production build a proper OAuth app)
- Store it in Lovable Cloud under Backend → Secrets as
MONDAY_API_TOKEN
⚠️ Personal token vs. OAuth: Personal tokens are tied to your user account – if you leave the company, every integration breaks. For production, always build a proper monday app with OAuth.
Step 2: Define field mapping
Every column in a monday board has a Column ID (not the column name!). Find it via API or in the UI under Column → ⋮ → Customize → Copy ID.
| Form field | monday column | Column ID | Column type |
|---|---|---|---|
name |
Item name | – | (item title) |
email |
email__1 |
email |
|
company |
Company | text__1 |
text |
phone |
Phone | phone__1 |
phone |
source |
Lead source | status__1 |
status |
message |
Notes | long_text__1 |
long_text |
Important: monday expects column_values as a JSON string – and every column type has its own format. text is a plain string, email is {"email":"...","text":"..."}, status is {"label":"..."}.
Step 3: Build the Edge Function
// supabase/functions/submit-lead-to-monday/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { z } from "https://deno.land/x/zod@v3.22.4/mod.ts";
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};
const LeadSchema = z.object({
name: z.string().trim().min(2).max(100),
email: z.string().trim().email(),
company: z.string().trim().max(200).optional(),
phone: z.string().trim().max(50).optional(),
source: z.enum(["website", "ads", "referral", "event"]),
message: z.string().trim().max(2000).optional(),
});
const BOARD_ID = 1234567890; // your board
const MONDAY_API = "https://api.monday.com/v2";
serve(async (req) => {
if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders });
try {
const parsed = LeadSchema.safeParse(await req.json());
if (!parsed.success) {
return new Response(JSON.stringify({ error: parsed.error.flatten() }), {
status: 400,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
const lead = parsed.data;
const token = Deno.env.get("MONDAY_API_TOKEN");
if (!token) throw new Error("MONDAY_API_TOKEN is not configured");
const columnValues = {
email__1: { email: lead.email, text: lead.email },
text__1: lead.company ?? "",
phone__1: lead.phone ? { phone: lead.phone, countryShortName: "US" } : null,
status__1: { label: lead.source },
long_text__1: { text: lead.message ?? "" },
};
const mutation = `
mutation ($boardId: ID!, $itemName: String!, $columnValues: JSON!) {
create_item(
board_id: $boardId,
item_name: $itemName,
column_values: $columnValues
) { id }
}
`;
const res = await fetch(MONDAY_API, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: token,
"API-Version": "2024-01",
},
body: JSON.stringify({
query: mutation,
variables: {
boardId: BOARD_ID,
itemName: lead.name,
columnValues: JSON.stringify(columnValues),
},
}),
});
const data = await res.json();
if (data.errors) {
console.error("monday error:", data.errors);
return new Response(JSON.stringify({ error: "monday API rejected request" }), {
status: 502,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
return new Response(JSON.stringify({ itemId: data.data.create_item.id }), {
status: 200,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
} catch (e) {
console.error("submit-lead-to-monday:", e);
return new Response(JSON.stringify({ error: "Internal error" }), {
status: 500,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
});Step 4: Call it from the frontend
// In your form's onSubmit
const { data, error } = await supabase.functions.invoke("submit-lead-to-monday", {
body: values,
});
if (error || data?.error) {
toast.error("Could not submit lead.");
return;
}
toast.success("Thanks! We'll be in touch within 24h.");
form.reset();No API token in your bundle, no CORS workarounds – the browser only talks to your Edge Function, which talks to monday.
Bidirectional updates: webhooks
Want sales to update lead status in monday and have it sync back to your CRM mirror or post to Slack? Set up a webhook in monday (Integrations → Webhooks → enter URL) that fires on change_column_value and calls a monday-webhook Edge Function. Classic flow:
- Sales sets status to "Qualified"
- monday fires webhook → Edge Function
- Edge Function posts in Slack channel and writes an audit log in your Lovable Cloud DB
Pitfalls (lessons learned)
| Problem | Cause | Fix |
|---|---|---|
Invalid JSON for column_values |
column_values as JS object instead of JSON string | JSON.stringify() the whole object |
| Status not set | Label doesn't exist in status type | Create labels in the board first or use index instead of label |
| Rate limit (429) | monday: 5000 calls/min per account | Edge Function with short backoff + retry |
| Personal token expires | Tied to a user | Build an OAuth app for production |
| Phone gets dropped | Phone type needs countryShortName | {phone, countryShortName: "US"} instead of plain string |
Bonus: subitems & updates
Want to also create a subitem task for the sales owner or post an update to the item? Both are second GraphQL mutations in the same Edge Function:
mutation ($parentId: ID!) {
create_subitem(parent_item_id: $parentId, item_name: "Initial outreach") { id }
}
mutation ($itemId: ID!, $body: String!) {
create_update(item_id: $itemId, body: $body) { id }
}Now your sales team has it all in one item: original request, planned action, history.
Conclusion
With ~80 lines of Edge Function code, every Lovable form becomes a fully-fledged monday lead – with your design, your logic, and no WorkForm embed. This is the custom counterpart to Part 1 and a textbook use case for the Lovable + monday stack.
👉 Next in the series: Part 5 – Smart Forms with AI · Part 6 – File Uploads in Lovable Forms
👉 Back: Part 1 · Part 2 · Part 3
Need help with the setup? Talk to us – we build monday integrations for CRM, HR and operations teams.
Lovable Forms Series
1 of 6 read · 17%Six articles taking you from tool choice to AI-powered forms with file uploads.
- PART 1Unread
Form Tools Compared
Tally, Typeform & monday WorkForms – when SaaS is worth it.
Read - PART 2Unread
Custom Build in Lovable
React Hook Form + zod + Lovable Cloud – the DIY foundation.
Read - PART 3Unread
Production-Ready Best Practices
Validation, GDPR, spam protection, UX feedback.
Read - PART 4ReadHere
Connect to monday.com
GraphQL API, Edge Function, lead → item on the board.
- PART 5Unread
Smart Forms with AI
Auto-complete, AI validation, conversational forms.
Read - PART 6Unread
File Uploads & Storage
Drag & drop, Supabase Storage, RLS, signed URLs.
Read
Reading progress is stored locally in your browser (localStorage).








