splitforms.com
All articles/ INTEGRATIONS9 MIN READPublished May 11, 2026

How to Send Form Submissions to monday.com in 2026

Send contact form submissions directly to monday.com in 2026 — webhook setup, item creation, custom column mapping, and a no-code path to your board.

✶ Written by
splitforms.com / blog

Founder of splitforms — the form backend API for developers. Writes about form UX, anti-spam, and shipping web apps without backend code.

Why pipe a form into monday.com at all

If your team already runs sales, hiring, support, or project intake in monday.com, the contact form on your marketing site is an awkward island. Submissions land in an inbox, someone copies the name and email into a monday item by hand, and three days later half of them are forgotten. The fix is plumbing — push each submission directly into a monday board as a new item, with the form fields mapped to columns. Then the work that already happens in monday (assignment, status, automations, notifications) picks up automatically.

You have two reasonable paths to do this in 2026. The first is a direct HTTP webhook from splitforms to your own tiny endpoint that calls the monday.com GraphQL API. The second is a no-code middleware route via Zapier or Make.com. This guide walks both, with real GraphQL mutations, column-mapping tables, and the activity-log gotchas nobody mentions until you hit them.

The form side is identical either way. You set up a splitforms form, point it at https://splitforms.com/api/submit, and configure an outbound webhook in the dashboard. From there, the "monday side" is what differs. Free tier on splitforms covers 1,000 submissions/month, free webhooks included — for context, see how the same flow works for Notion or Airtable.

What you need before you start

Both paths share the same prerequisites. Get these set up first and the rest is mechanical:

  1. A splitforms account and access key. Sign up at splitforms.com/login, copy the access key from the dashboard.
  2. An HTML form on your site with action="https://splitforms.com/api/submit" and the access key as a hidden input.
  3. A monday.com board with the columns you actually need — at minimum a name column (default), an email column, a status column, and any custom columns for priority, source, or notes.
  4. A monday.com API token. Avatar (top right) → Developers → My Access Tokens → copy. Treat this like a password. It has full access to every board the user can see.
  5. Your board ID and column IDs. Board ID is in the URL. Column IDs aren't the labels — see the introspection query below.

Here's the HTML form you'll wire up, identical to the one in our free contact form template:

<form action="https://splitforms.com/api/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
  <input type="text"  name="name"     required />
  <input type="email" name="email"    required />
  <input type="text"  name="company"  />
  <select name="priority">
    <option>Low</option><option>Medium</option><option>High</option>
  </select>
  <textarea name="message" required></textarea>
  <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" />
  <button type="submit">Send</button>
</form>

This form has five fields you'll map to columns: name (item name), email (email column), company (text column), priority (status or dropdown column), and message (long text column).

Path 1: splitforms webhook → monday GraphQL API

This is the path you want if you already have somewhere to run code — Vercel, Cloudflare Workers, AWS Lambda, a Node server, a Python Flask app, whatever. You're writing one HTTP endpoint. splitforms POSTs to it whenever a form is submitted, you call the monday GraphQL API once to create an item, you return 200. Total wall-clock from form submit to monday board update: about 800 milliseconds.

The reason this beats polling: webhooks are free on splitforms, push is instant, and you're only making API calls when there's actually work to do. For a deeper walkthrough of the webhook envelope splitforms sends, see send form data to a webhook.

Set up the outbound webhook in splitforms

  1. Dashboard → your form → Integrations → Webhooks → Add webhook
  2. Paste your endpoint URL, e.g. https://your-app.vercel.app/api/monday-webhook
  3. (Optional) copy the signing secret — you'll use it to verify the X-Splitforms-Signature header on inbound requests
  4. Save. splitforms sends a test event you should see hit your endpoint within seconds.

The GraphQL mutation that creates the item

monday.com's entire API is a single GraphQL endpoint: https://api.monday.com/v2. You authenticate with an Authorization header containing the raw token (no Bearer prefix — monday doesn't use OAuth-style bearer scheme by default for personal tokens). Here's the mutation that creates a new item with column values populated:

mutation CreateLeadItem(
  $boardId: ID!,
  $itemName: String!,
  $columnValues: JSON!
) {
  create_item(
    board_id: $boardId,
    item_name: $itemName,
    column_values: $columnValues,
    create_labels_if_missing: true
  ) {
    id
    name
    created_at
  }
}

The trick is column_values. It's a JSON-encoded string (yes, a string of JSON — monday is opinionated about this) where keys are column IDs and values match each column type's shape. Text columns want a plain string; status wants { "label": "New" }; person wants { "personsAndTeams": [{"id": 12345, "kind": "person"}] }; email wants { "email": "x@y.com", "text": "x@y.com" }.

The webhook handler (Node / TypeScript)

// /api/monday-webhook (Vercel / Next.js route handler)
export async function POST(req: Request) {
  const submission = await req.json();
  // splitforms payload shape:
  // { form_id, fields: { name, email, company, priority, message }, submitted_at }
  const { fields } = submission;

  const columnValues = {
    email__1:    { email: fields.email, text: fields.email },
    text__1:     fields.company || "",
    status:      { label: fields.priority || "Low" },
    long_text__1:{ text: fields.message },
    date__1:     { date: new Date().toISOString().slice(0, 10) },
  };

  const query = `
    mutation (\$boardId: ID!, \$itemName: String!, \$cv: JSON!) {
      create_item(board_id: \$boardId, item_name: \$itemName, column_values: \$cv) {
        id
      }
    }`;

  const r = await fetch("https://api.monday.com/v2", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": process.env.MONDAY_API_TOKEN!,
      "API-Version": "2024-10",
    },
    body: JSON.stringify({
      query,
      variables: {
        boardId: process.env.MONDAY_BOARD_ID,
        itemName: fields.name,
        cv: JSON.stringify(columnValues),
      },
    }),
  });

  const data = await r.json();
  if (data.errors) {
    console.error("monday error:", data.errors);
    return new Response("error", { status: 500 });
  }
  return new Response("ok");
}

Three things to flag here. First, the Authorization header is the raw token, no prefix. Second, the API-Version header pins you to a specific monday API version — leave it off and you get the "default" version which can shift under you. Third, column_values is double-JSON-encoded: it's a JSON object whose value at cv is itself a JSON-stringified object. This trips up everyone the first time.

Mapping form fields to monday column types

This is the table you'll wish you had on your first attempt. Each monday column type wants a different shape inside column_values:

Column typeForm field exampleShape in column_values
Item name (built-in)namePassed as item_name arg, not in column_values
Text (short)company"text__1": "Acme Co"
Long textmessage"long_text__1": { &quot;text&quot;: &quot;...&quot; }
Emailemail"email__1": { &quot;email&quot;: &quot;x@y.com&quot;, &quot;text&quot;: &quot;x@y.com&quot; }
Phonephone"phone__1": { &quot;phone&quot;: &quot;+15551234567&quot;, &quot;countryShortName&quot;: &quot;US&quot; }
Status (dropdown)priority"status": { &quot;label&quot;: &quot;High&quot; }
Date(server-set)"date__1": { &quot;date&quot;: &quot;2026-05-11&quot; }
People (assignee)(routing rule)"person": { &quot;personsAndTeams&quot;: [{ &quot;id&quot;: 12345, &quot;kind&quot;: &quot;person&quot; }] }
Linkwebsite"link__1": { &quot;url&quot;: &quot;https://...&quot;, &quot;text&quot;: &quot;Site&quot; }

To find your real column IDs, run this once in the monday API Playground (Developers → API Playground):

query {
  boards(ids: 1234567890) {
    columns { id title type }
  }
}

You'll get back the actual IDs (text__1, email__1, etc.) along with their titles and types. Copy those into your handler. If you add columns later, re-run this query and update the map.

Status column gotcha: create_labels_if_missing

If your form lets users type a priority that doesn't exist as a status label yet ("Critical" when the board only has "Low/Medium/High"), the mutation fails unless you pass create_labels_if_missing: true. Even then, the new label gets created with a random color, which usually looks bad. Cleaner pattern: lock the form to a <select> with exactly the labels that exist on the board.

Path 2: splitforms → Make.com or Zapier → monday (no code)

If you don't want to deploy code, you can chain splitforms to monday through Make.com (cheaper, more powerful) or Zapier (more polish, more expensive). The mechanics are identical on both: splitforms posts to a generic webhook URL exposed by the automation platform, the platform parses the payload, and a monday module creates the item.

Make.com setup (recommended)

  1. Make → Create a new scenario → trigger: Webhooks → Custom webhook. Copy the generated URL.
  2. splitforms dashboard → form → Webhooks → paste the Make URL → save.
  3. Submit a test form. Make.com's webhook module captures the payload structure automatically — you'll see all your fields show up as variables.
  4. Add a second module: monday.com → Create an Item. Connect with your API token.
  5. Pick the board, then map each column to the matching webhook variable. Make's UI handles the column-type formatting for you.
  6. Activate the scenario. Done — under 30 minutes end to end.

Zapier setup

Same flow with a Zapier wrapper. Trigger: Webhooks by Zapier → Catch Hook. Action: monday.com → Create Item. Field-by-field mapping. The main difference is cost: Zapier counts each submission as a task (~$0.02–$0.03 at typical plan tiers), while Make charges by operations and runs cheaper at volume.

When to use this path

Three cases where path 2 wins outright: (1) you don't want to maintain code, (2) you want to fan a single submission out to monday and Slack and Gmail and a Google Sheet without writing five integrations, or (3) someone non-technical needs to be able to edit the mapping. Otherwise path 1 is faster, cheaper, and has zero per-submission ceiling.

Activity logs, attribution, and the "Forms Bot" pattern

monday.com's activity log on every item records who created it, when, and every column-value change since. When you create items via the API, the "created by" field is the user who owns the API token. If that token belongs to you personally, every form submission shows you as the creator. That's usually wrong — you want the log to make it obvious the item came from a form, not from a human typing.

The clean pattern is to create a dedicated bot user inside your monday workspace:

  1. monday admin → Users → Invite member → use an email like forms-bot@yourcompany.com
  2. Log in as that user (most monday workspaces let you assign a member seat to a service account)
  3. Generate an API token under that account
  4. Use that token in your webhook handler

Now the activity log shows "Created by Forms Bot" for every form-originated item. You can also set up monday automations that fire only when the creator is Forms Bot — e.g., auto-assign to the on-call sales rep, send a Slack notification, or move the item to a triage group. The board stays clean and the audit trail is honest.

One more log-related win: every monday item also has an updates section (basically a comment thread per item). You can post the raw form submission as an update via the create_update mutation, which gives you a verbatim record of what the user typed even if someone later edits the column values. That's genuinely useful for support and sales contexts where the original message matters.

How to test the whole pipeline end-to-end

Before you point real traffic at this, run through every step with a known-good test submission. The failure modes are subtle and silent.

  1. Hit your webhook endpoint directly with a curl that mimics splitforms' payload shape. Verify the monday mutation runs and an item appears.
  2. Submit the form on your live site. Use a recognizable test name ("Test User 2026-05-11") and a real email so spam classification doesn't auto-block it.
  3. Check splitforms' submissions tab — the submission should show up there within ~1 second. Click into it to see the webhook delivery log: was it 200? 4xx? Timeout?
  4. Check monday.com. The new item should appear in the board within a few seconds of the form submit.
  5. Verify column values populated correctly. Status label, email field, long text, date — open the item and confirm each column has data, not a blank.
  6. Resubmit with intentionally broken data. Wrong status label, missing email, empty required field. See what error you get and add server-side validation in your webhook handler if needed.

If a submission lands in splitforms but doesn't reach monday, the webhook delivery log will show why — most commonly a 401 from monday (bad token), a 400 (malformed column_values shape), or a timeout from your endpoint cold-starting. Fix and replay the webhook from the splitforms dashboard.

Common errors and how to fix them

  • 401 Unauthorized from monday. Your token is wrong, expired, or doesn't have access to the board. Regenerate, paste again (watch for leading whitespace), confirm the token user is a member of the workspace that owns the board.
  • "ColumnValueException: invalid value". The column_values shape is wrong for that column type. Status wants {label: "..."}, not a plain string. Long text wants {text: "..."}, not a string. Re-check the mapping table above.
  • Item created but column is blank. Your column ID is wrong. Run the introspection query again — IDs include the __1 suffix, they're not the human label.
  • Status label doesn't exist. Either add create_labels_if_missing: true to the mutation, or lock your form's priority dropdown to the labels that already exist on the board.
  • Rate limit (429). monday's complexity budget is shared across the token. If you're hitting limits on a contact form, something else is also hammering the token — investigate before increasing concurrency.
  • splitforms webhook returns 500 from your endpoint. Your handler is throwing. Check logs. Most common cause: missing env var (MONDAY_API_TOKEN or MONDAY_BOARD_ID not set in production).
  • Make.com / Zapier scenario silently stopped. Both pause scenarios after a streak of errors. Check the platform dashboard, fix the underlying issue, reactivate.

Next steps

FAQ

Do I need a paid monday.com plan to use the API?

No. The monday.com GraphQL API works on the Free and Basic plans, with rate limits scaling up at Standard and Pro. As of 2026-05, every plan with a board you control allows API token generation. The limit you'll actually hit first is API complexity (10M points/minute on most plans), not plan gating. For a contact form pushing one item per submission, you'll never come close to the cap.

Where do I find the monday.com board ID and column IDs?

The board ID is in the URL when you open the board — splitforms.monday.com/boards/1234567890 means the board ID is 1234567890. Column IDs are harder: they're not the human-readable column names. Run a one-time GraphQL query `query { boards(ids: 1234567890) { columns { id title type } } }` in the API Playground and you'll get the real IDs like `status`, `text__1`, `email__1`. Copy those into your webhook handler.

Why use a webhook instead of polling the monday.com API on a cron?

Polling wastes API points, adds latency (your cron only runs every 5–15 minutes), and breaks when the cron host hiccups. A webhook is push: splitforms hits your endpoint within ~1 second of a submission, you do one mutation to monday, done. No state to track, no missed submissions, no API points burned on empty polls. Webhooks are also free on splitforms — Formspree and most competitors paywall this.

What's the difference between path 1 (direct webhook) and path 2 (Make/Zapier)?

Path 1 is one HTTP endpoint that you control, written in 40 lines of code, with sub-second latency and no per-task cost. Path 2 trades that ~30 minutes of setup for a visual builder, zero hosting, and Make/Zapier task fees (~$0.01–$0.03 per submission at typical volume). Use path 1 if you already have a server or serverless function. Use path 2 if you don't want to touch code or you need to fan out to 5+ other tools at the same time.

Can I update an existing item instead of creating a new one?

Yes, but you need a way to match the submission to an existing item. The common pattern is to use the submitter's email as a lookup key: query `items_page_by_column_values` to see if an item with that email exists, then either `change_multiple_column_values` (update) or `create_item` (insert). This is useful for lead deduplication. For most contact-form-to-CRM flows, creating a fresh item per submission is simpler and lets you see every touchpoint.

What happens to file uploads from the form?

splitforms stores form file uploads and exposes them as URLs in the webhook payload. monday.com's file column accepts a public URL via the GraphQL `add_file_to_column` mutation, but uploading from a URL requires a two-step dance: download the file, then upload its bytes. Simpler workaround: drop the splitforms-hosted file URL into a text column and let assignees click through. If you need files actually inside monday, use the Make.com path with its built-in file-fetch action.

How do I stop spam submissions from polluting my monday.com board?

Filter spam at splitforms before the webhook fires. splitforms includes free AI-powered spam detection — see /blog/ai-form-spam-detection — and a honeypot field. Spam submissions get tagged and never trigger the outbound webhook, so your monday board stays clean. If you want a second line of defense, validate the submission server-side in your webhook handler before calling the monday mutation (require a real-looking email, reject submissions with > 5 links in the message, etc.).

Will the activity log show who created the item?

monday.com's activity log will show whichever user owns the API token you used — typically a service-account user or whoever generated the token. If you want each item to look like it was created by the person who submitted, that's not possible via the API (monday won't attribute creation to a non-user). The standard pattern is to create a dedicated 'Forms Bot' user, generate the token under that account, and use a Person column to assign the submission to a real human owner.

About the author
✻ ✻ ✻

Get your free contact form API key in 60 seconds.

1,000 free form submissions per month. No credit card. No SDK, no PHP, no plugin. Drop one POST endpoint in your form and submissions land in your inbox.

Generate access key →Read the docs
v0.1 · founders pricing locked in · early access open