How to Build a Self-Service Project Intake Form with a Webhook and AI Triage
Let a public intake form POST a prospect's project details to a Spojit Webhook trigger, have an AI Agent score fit and estimate effort as structured JSON, then either create a qualified-lead item in Monday.com or send a polite decline, and return a confirmation reference to the form in the same request.
What This Integration Does
Professional services teams (agencies, consultancies, studios) lose hours triaging inbound project requests by hand: reading each submission, judging whether it is a fit, estimating rough effort, and only then deciding where it goes. This Spojit workflow puts that triage on autopilot. A simple public form on your website posts the prospect's name, email, company, budget, timeline, and a free-text project description to a workflow URL. An Agent-mode Connector node reads the description, scores how well the request fits your services, estimates a rough effort band, and writes its reasoning. Qualified leads land on a Monday.com board ready for a human to pick up; weak-fit requests get a courteous decline. The form caller gets a confirmation reference back in the same HTTP response, so the prospect sees an instant "thanks, we have your request" with a tracking code.
The run model is synchronous. The form's HTTP POST starts one execution; Spojit verifies the request with a signing connection, parses the JSON body, runs the AI scoring step, branches on the score with a Condition node, performs the Monday.com action or the decline, and finishes by returning a JSON payload to the still-open HTTP connection via a Response node. Each submission is its own execution, so re-running or retrying one submission never touches another. Because the Webhook trigger returns immediately once the Response node fires, keep the work between trigger and response lean. If the same submission is sent twice, opt-in deduplication on an event-id header prevents a duplicate Monday.com item.
Prerequisites
- A Monday.com connection added under Connections, with access to the board that will hold qualified leads. Note the board's
id(open the board and read it from the URL or list boards with the connector). - A board with columns you want to populate, for example an email column, a status column, and a long-text notes column. You will map submission fields and the AI's reasoning into these.
- A signing connection for the Webhook trigger. Use the Spojit scheme for the simplest HMAC setup, or Custom if your form host signs requests its own way. See Setting Up a Webhook Connection.
- An optional Slack connection if you want a real-time alert in a channel when a high-value lead arrives.
- Your intake form (any site or form builder) able to send an HTTP POST with a JSON body and the signing header.
Step 1: Add the Webhook trigger and define the form payload
Create a new workflow, then set the Trigger node type to Webhook. Choose the signing connection from your prerequisites so Spojit verifies each request by HMAC. Copy the generated workflow URL into your form's submit action. The trigger output is the parsed JSON body, available downstream as {{ input }}. Design your form to post a body like this:
{
"name": "Dana Okoye",
"email": "dana@brightlabs.io",
"company": "Bright Labs",
"budget": "15000",
"timeline": "6-8 weeks",
"description": "We need a customer portal with SSO, a billing dashboard, and a public API. Existing stack is Next.js and Postgres."
}
To avoid duplicate items if a prospect double-submits, have your form send a stable event-id header (for example a per-submission UUID) and enable deduplication on the trigger. The trigger responds with 202 and an executionId by default, but because this workflow ends with a Response node, your final JSON reply replaces that. For trigger setup details, see Setting Up a Webhook Trigger.
Step 2: Score fit and estimate effort with a Connector node in Agent mode
Add a Connector node and switch it to Agent mode. Agent mode lets the agent reason over the free-text description rather than forcing a single fixed tool call. In the prompt, pass the submission fields and ask for a fit score, an effort band, and a short justification. Reference the trigger output with handlebars:
You are triaging an inbound project request for a professional services
agency that builds web applications and integrations.
Submission:
- Company: {{ input.company }}
- Budget (USD): {{ input.budget }}
- Desired timeline: {{ input.timeline }}
- Project description: {{ input.description }}
Score how well this fits our services from 0 to 100, where 0 is a poor
fit and 100 is an ideal fit. Estimate the rough build effort as one of
"small", "medium", or "large". Give a one-sentence justification a sales
lead can read at a glance.
Set the node's Output Variable to triage so later steps can read its result. To learn when Agent mode is the right call versus a deterministic tool, read How to Choose Between Agent Mode and Direct Mode.
Step 3: Force structured JSON with a Response Schema
Free-text from an AI step is hard to branch on reliably. In the same Agent mode Connector node, open Response Schema and define the exact JSON shape you want back. This makes the output deterministic to read with a Condition node and to map into Monday.com columns. Use a schema like:
{
"type": "object",
"properties": {
"fitScore": { "type": "integer", "minimum": 0, "maximum": 100 },
"effort": { "type": "string", "enum": ["small", "medium", "large"] },
"justification": { "type": "string" }
},
"required": ["fitScore", "effort", "justification"]
}
With the schema applied, downstream steps can read {{ triage.fitScore }}, {{ triage.effort }}, and {{ triage.justification }} as clean fields. For more on why this matters, see How to Use Structured Output for Reliable AI Data Extraction.
Step 4: Branch on the score with a Condition node
Add a Condition node after the triage step. Set the condition to compare the AI's score against your qualification threshold, for example {{ triage.fitScore }} is greater than or equal to 60. The true output is the qualified path (create a Monday.com item); the false output is the decline path. Keeping the threshold in the Condition node, rather than baked into the prompt, lets you tune qualification without re-touching the AI step. For branching mechanics, see Using Condition Nodes.
Step 5: Create a qualified-lead item in Monday.com (true branch)
On the Condition's true output, add a Connector node in Direct mode on the Monday.com connector and pick the create-item tool. Direct mode is right here because the action is a single, predictable call with no AI cost. Map the fields:
- Board: your qualified-leads board
idfrom the prerequisites. - Item name:
{{ input.company }} - {{ input.name }}so the board reads cleanly. - Column values: map the email column to
{{ input.email }}, a budget or timeline column to{{ input.budget }}and{{ input.timeline }}, and a long-text notes column to a blend of the description and the AI's reasoning, for exampleFit {{ triage.fitScore }} ({{ triage.effort }}): {{ triage.justification }} -- {{ input.description }}.
Set the node's Output Variable to created so the new item id is available for the confirmation reference. Optionally add a create-update call to post the AI justification as an update on the item, or a Slack send-message call to your sales channel for high scores. For the connector reference, see the Monday.com connector article.
Step 6: Send a polite decline (false branch)
On the Condition's false output, add a Send Email node. It sends from Spojit's built-in mail service, so no connection is needed. Set Recipients to {{ input.email }}, write a courteous Subject and Body explaining the request is not a fit right now, and leave Reply-To as the workflow owner so the prospect can respond to a human. Note that external recipients must be on your org allowlist under Settings -> General -> Email recipients. If you would rather send from your own domain, use the Resend or SMTP connector instead. See Using Send Email Nodes and Configuring the Email Allowlist.
Step 7: Return a confirmation reference with a Response node
End both branches at a Response node so the form caller gets an immediate reply in the same HTTP request. Return a small JSON object the form can show the prospect, including a tracking reference. On the qualified path the reference can be the new Monday.com item id; on the decline path, return a generic acknowledgement. A single Response node fed by both branches works well:
{
"status": "received",
"reference": "{{ created.id }}",
"fitScore": "{{ triage.fitScore }}",
"message": "Thanks, we have your project request and will be in touch."
}
Keep the response body free of internal reasoning you would not want a prospect to read. For how synchronous replies work, see Using Response Nodes.
Tips
- Tune the qualification threshold in the Condition node, not the prompt. Start strict (for example 70), watch a few days of submissions, then relax it once the AI's scoring tracks your judgment.
- Add a Slack
send-messagestep on high scores (for example{{ triage.fitScore }}over 85) to ping your sales channel instantly, so big opportunities never wait in a board queue. - Ask Miraxa, the intelligent layer across your automation, to scaffold the branch for you: "Add a Condition node that checks if
{{ triage.fitScore }}is at least 60 and connect the true branch to a Monday.com create-item node." Then fine-tune field mapping in the properties panel. - Keep the description prompt focused on your actual services so the score reflects real fit rather than generic project size.
Common Pitfalls
- Skipping the Response Schema. Without it, the AI may return prose, and
{{ triage.fitScore }}will not resolve cleanly, breaking the Condition. Always define the schema in Step 3. - Forgetting the signing connection. A Webhook trigger needs a valid HMAC signing connection; an unsigned or wrongly signed POST is rejected before your workflow runs.
- Treating budget as a number when the form sends a string. If your form posts
"budget": "15000", compare and map it as text, or normalize it in a Transform step first. - Doing slow work before the Response node. The form connection stays open until the Response fires, so a heavy step between trigger and response can make the form appear to hang. Keep the synchronous path short.
- Missing email allowlist entries. The decline email will not send to an external prospect unless their address (or domain) is permitted under Settings -> General -> Email recipients.
Testing
Validate on a small scope before pointing your live form at the URL. Use a tool that can POST JSON (or a hidden test page) to send a single submission with the signing header, and watch the run in your execution history. Confirm the triage step returns clean JSON matching your schema, that a high-budget realistic submission takes the true branch and creates a Monday.com item, and that a deliberately weak submission takes the false branch and sends the decline. Check the HTTP response your test client receives contains the reference and message. Once a few representative submissions behave correctly, swap in your real form action and enable deduplication so a double-click never creates two items.