splitforms.com
LEAD CAPTURE · CONTACT FORM TEMPLATE

SaaS Demo Request Form (Sales Lead Capture)

Demo requests are the highest-intent leads a SaaS gets. The form qualifies on company size and use case so the AE walks into the demo prepared, not improvising.

1,000/mo free·no card·works on any host
form.htmlhtml43 lines
01<form action="https://splitforms.com/api/submit" method="POST">
02 <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
03 <input type="hidden" name="subject" value="New demo request">
04
05 <label for="name">Full name *</label>
06 <input id="name" type="text" name="name" placeholder="Hans Becker" required>
07 <label for="work_email">Work email *</label>
08 <input id="work_email" type="email" name="work_email" placeholder="hans@company.com" required>
09 <label for="phone">Phone</label>
10 <input id="phone" type="tel" name="phone" placeholder="+1 415 555 0142">
11 <label for="company">Company *</label>
12 <input id="company" type="text" name="company" placeholder="Kraft GmbH" required>
13 <label for="role">Your role *</label>
14 <select id="role" name="role" required>
15 <option value="">Choose…</option>
16 <option>Founder / CEO</option>
17 <option>Engineering</option>
18 <option>Product</option>
19 <option>Marketing</option>
20 <option>Sales</option>
21 <option>Operations</option>
22 <option>Other</option>
23 </select>
24 <label for="team_size">Team size *</label>
25 <select id="team_size" name="team_size" required>
26 <option value="">Choose…</option>
27 <option>1–10</option>
28 <option>11–50</option>
29 <option>51–250</option>
30 <option>250+</option>
31 </select>
32 <label for="use_case">What are you trying to solve? *</label>
33 <textarea id="use_case" name="use_case" placeholder="Replacing Formspree across 14 client sites." required></textarea>
34
35 <!-- honeypot — bots fill every field -->
36 <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off">
37
38 <button type="submit">Send</button>
39</form>
40
41<p style="margin-top:12px;font-size:11px;color:#888;text-align:right">
42 Powered by <a href="https://splitforms.com" style="color:#888;text-decoration:none" target="_blank" rel="noopener">splitforms</a>
43</p>
1,000
submissions / mo, free
7
fields, ready to ship
5
code outputs
60s
from copy to inbox
SaaS Demo Request Form (Sales Lead Capture) — example splitforms template with submissions inbox
§ 01Why it mattersthe qualifying-fields argument

Demo requests cost the SaaS team real money — an AE spends 30-45 minutes on each call. Unqualified demos burn money. The form qualifies on company size, role, use case, current tools, and timeline. ICP-fit leads (right company size, right industry, right pain point) get the AE calendar slot; off-ICP get nurtured via email. The qualifying data also pre-loads the CRM so the AE walks in knowing the prospect's stack and use case — demo close rates climb 30-50% on prepared calls vs cold ones. Most B2B SaaS uses Calendly / Chili Piper for the booking step; the form is the gating layer in front of the calendar.

Webhooks into HubSpot / Salesforce / Close · Chili Piper / Calendly routing.
✦ at a glance
  • Demo request · 7 fields
  • HTML, JS, React, PHP, cURL outputs
  • One POST endpoint, no SDK
  • Honeypot + classifier, no CAPTCHA
§ 02Live previewinteractive · sandboxed · no key required

See exactly what your visitors see — and you’ll receive.

Left: the rendered form, fully interactive in a sandboxed iframe. Right: the email and dashboard view that lands the moment a visitor submits.

preview · demo-request-formlocalhost:3000
✦ what you’ll see in your inbox

Every submission becomes an email plus a dashboard row. The fields below are the exact payload your form will send. Reply-to is wired to the visitor’s email so hitting reply goes back to them.

dashboard · new submission14ms · 200 OK
SUBJECT · New demo request
Full name
Maya Iyer
Work email
maya@studio71.co
Phone
+1 415 555 0142
Company
Studio 71
Your role
Founder / CEO
Team size
1–10
What are you trying to solve?

Iframe is sandboxed — submit doesn’t actually fire. Get your access key to wire it up live.

§ 03Three steps3 steps · ~60 seconds

Generate, embed, receive.

Three actions stand between you and your first lead. None of them require a backend, a database, or a CAPTCHA library.

STEP 01GENERATE

Capture firmographic data

Required: name, work email, company, role, team size, use case (1-3 sentences). Optional: current tools, timeline, budget range.

Create your form
key=sk_live_••••••••
STEP 02EMBED

Route by ICP fit

Webhook branches on company size and role — ICP-fit leads (e.g. 50+ team, ops/eng decision-maker) book the AE calendar instantly via Chili Piper / Calendly. Off-ICP get nurtured via email instead of burning AE time.

snippethtml
<form action="https://splitforms.com/api/submit" method="POST">
  …
</form>
STEP 03RECEIVE

Push to CRM

Webhook to HubSpot / Salesforce / Close / Pipedrive with all qualifying fields prefilled. AE arrives at the demo prepared with the prospect's stack and use case in their notes.

inbox · 1 newjust now
FROM contact@yoursite.com
New demo request
Maya Iyer maya@studio71.co
Loved your last open house in Hayes — looking for similar with parking. Pre-approved through Wells Fargo.
§ 04Copy & ship5 languages · same endpoint

Five outputs. One backend.

HTML by default. Click open the language you ship in — every variant POSTs to the same /api/submit endpoint.

01HTMLform.html43 lines
<form action="https://splitforms.com/api/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
  <input type="hidden" name="subject" value="New demo request">

  <label for="name">Full name *</label>
  <input id="name" type="text" name="name" placeholder="Hans Becker" required>
  <label for="work_email">Work email *</label>
  <input id="work_email" type="email" name="work_email" placeholder="hans@company.com" required>
  <label for="phone">Phone</label>
  <input id="phone" type="tel" name="phone" placeholder="+1 415 555 0142">
  <label for="company">Company *</label>
  <input id="company" type="text" name="company" placeholder="Kraft GmbH" required>
  <label for="role">Your role *</label>
  <select id="role" name="role" required>
    <option value="">Choose…</option>
    <option>Founder / CEO</option>
    <option>Engineering</option>
    <option>Product</option>
    <option>Marketing</option>
    <option>Sales</option>
    <option>Operations</option>
    <option>Other</option>
  </select>
  <label for="team_size">Team size *</label>
  <select id="team_size" name="team_size" required>
    <option value="">Choose…</option>
    <option>1–10</option>
    <option>11–50</option>
    <option>51–250</option>
    <option>250+</option>
  </select>
  <label for="use_case">What are you trying to solve? *</label>
  <textarea id="use_case" name="use_case" placeholder="Replacing Formspree across 14 client sites." required></textarea>

  <!-- honeypot — bots fill every field -->
  <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off">

  <button type="submit">Send</button>
</form>

<p style="margin-top:12px;font-size:11px;color:#888;text-align:right">
  Powered by <a href="https://splitforms.com" style="color:#888;text-decoration:none" target="_blank" rel="noopener">splitforms</a>
</p>
02JavaScriptform.js59 lines
<form id="lf-form">
  <label for="name">Full name *</label>
  <input id="name" type="text" name="name" placeholder="Hans Becker" required>
  <label for="work_email">Work email *</label>
  <input id="work_email" type="email" name="work_email" placeholder="hans@company.com" required>
  <label for="phone">Phone</label>
  <input id="phone" type="tel" name="phone" placeholder="+1 415 555 0142">
  <label for="company">Company *</label>
  <input id="company" type="text" name="company" placeholder="Kraft GmbH" required>
  <label for="role">Your role *</label>
  <select id="role" name="role" required>
    <option value="">Choose…</option>
    <option>Founder / CEO</option>
    <option>Engineering</option>
    <option>Product</option>
    <option>Marketing</option>
    <option>Sales</option>
    <option>Operations</option>
    <option>Other</option>
  </select>
  <label for="team_size">Team size *</label>
  <select id="team_size" name="team_size" required>
    <option value="">Choose…</option>
    <option>1–10</option>
    <option>11–50</option>
    <option>51–250</option>
    <option>250+</option>
  </select>
  <label for="use_case">What are you trying to solve? *</label>
  <textarea id="use_case" name="use_case" placeholder="Replacing Formspree across 14 client sites." required></textarea>
  <button type="submit">Send</button>
</form>

<p style="margin-top:12px;font-size:11px;color:#888;text-align:right">
  Powered by <a href="https://splitforms.com" style="color:#888;text-decoration:none" target="_blank" rel="noopener">splitforms</a>
</p>

<script>
  document.getElementById('lf-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const data = new FormData(e.target);
    data.set('access_key', 'YOUR_ACCESS_KEY');
    data.set('subject', 'New demo request');

    const res = await fetch('https://splitforms.com/api/submit', {
      method: 'POST',
      body: data,
      headers: { Accept: 'application/json' },
    });

    const json = await res.json();
    if (json.success) {
      e.target.reset();
      alert('Sent!');
    } else {
      alert('Error: ' + (json.message || 'Try again'));
    }
  });
</script>
03React / Next.jsForm.tsx74 lines
'use client';

import { useState, type FormEvent } from 'react';

export default function DemoRequestForm() {
  const [status, setStatus] = useState<'idle' | 'sending' | 'sent' | 'error'>('idle');

  async function onSubmit(e: FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setStatus('sending');

    const data = new FormData(e.currentTarget);
    data.set('access_key', 'YOUR_ACCESS_KEY');
    data.set('subject', 'New demo request');

    const res = await fetch('https://splitforms.com/api/submit', {
      method: 'POST',
      body: data,
      headers: { Accept: 'application/json' },
    });

    const json = await res.json();
    setStatus(json.success ? 'sent' : 'error');
    if (json.success) e.currentTarget.reset();
  }

  if (status === 'sent') return <p>Thanks — we&rsquo;ll be in touch.</p>;

  return (
    <>
    <form onSubmit={onSubmit}>
      <label htmlFor="name">Full name *</label>
      <input id="name" type="text" name="name" placeholder="Hans Becker" required />
      <label htmlFor="work_email">Work email *</label>
      <input id="work_email" type="email" name="work_email" placeholder="hans@company.com" required />
      <label htmlFor="phone">Phone</label>
      <input id="phone" type="tel" name="phone" placeholder="+1 415 555 0142" />
      <label htmlFor="company">Company *</label>
      <input id="company" type="text" name="company" placeholder="Kraft GmbH" required />
      <label htmlFor="role">Your role *</label>
      <select id="role" name="role" required>
        <option value="">Choose…</option>
        <option>Founder / CEO</option>
        <option>Engineering</option>
        <option>Product</option>
        <option>Marketing</option>
        <option>Sales</option>
        <option>Operations</option>
        <option>Other</option>
      </select>
      <label htmlFor="team_size">Team size *</label>
      <select id="team_size" name="team_size" required>
        <option value="">Choose…</option>
        <option>1–10</option>
        <option>11–50</option>
        <option>51–250</option>
        <option>250+</option>
      </select>
      <label htmlFor="use_case">What are you trying to solve? *</label>
      <textarea id="use_case" name="use_case" placeholder="Replacing Formspree across 14 client sites." required />

      <button type="submit" disabled={status === 'sending'}>
        {status === 'sending' ? 'Sending…' : 'Send'}
      </button>

      {status === 'error' && <p>Something went wrong. Try again.</p>}
    </form>

      <p style={{ marginTop: 12, fontSize: 11, color: '#888', textAlign: 'right' }}>
        Powered by <a href="https://splitforms.com" target="_blank" rel="noopener" style={{ color: '#888', textDecoration: 'none' }}>splitforms</a>
      </p>
    </>
  );
}
04PHPsubmit.php28 lines
<?php
// Drop into a PHP page. Receives a form POST and proxies it to splitforms.com.
// Useful when you want to add server-side validation or rate limiting.

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $allowed = ['name', 'work_email', 'phone', 'company', 'role', 'team_size', 'use_case'];
    $payload = ['access_key' => 'YOUR_ACCESS_KEY'];
    $payload['subject'] = 'New demo request';

    foreach ($allowed as $f) {
        if (isset($_POST[$f])) $payload[$f] = $_POST[$f];
    }

    $ch = curl_init('https://splitforms.com/api/submit');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept: application/json']);
    $response = curl_exec($ch);
    $status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    header('Content-Type: application/json');
    http_response_code($status);
    echo $response;
    exit;
}
?>
05cURLtest.sh11 lines
curl -X POST https://splitforms.com/api/submit \
  -H "Accept: application/json" \
  -d "access_key=YOUR_ACCESS_KEY" \
  -d "subject=New demo request" \
  -d "name=Jane Builder" \
  -d "work_email=jane@example.com" \
  -d "phone=+15555555555" \
  -d "company=Jane Builder" \
  -d "role=Founder / CEO" \
  -d "team_size=1–10" \
  -d "use_case=Hello from cURL" 

Replace YOUR_ACCESS_KEY with the key from your dashboard. That’s the only edit.

§ 06FAQ4 answered

Things people ask before they ship.

Direct answers, no marketing fluff. Missing one? Email hello@splitforms.com.

01Should I require a work email?
Yes — block free email domains (gmail / yahoo / hotmail) on the email field. Free-email demos are 80% tire-kickers; work email is a basic ICP filter. Use a regex pattern or a real-time validator like ZeroBounce / Hunter to enforce.
02Can I integrate with Chili Piper / Calendly?
Yes — Chili Piper natively pulls form data and routes to the right AE based on territory / industry / company size. Calendly for Sales Teams supports round-robin routing too. Webhook the qualified lead to the booker; the unqualified ones bypass the calendar.
03How do I handle off-ICP demo requests?
Auto-respond with self-serve resources — a recorded demo, free trial signup, pricing page link, customer case studies. Add to a long-term nurture sequence. Don't burn AE time but don't lose the lead either.
04What about anti-spam / bot demo requests?
Hidden honeypot plus splitforms' classifier catches obvious bots. For sophisticated B2B form-stuffers (link-building bots filling out contact forms), add Cloudflare Turnstile or hCaptcha as a second layer. Free email blocking also catches most.
✻ ✻ ✻

Ship your saas demo request form (sales lead capture) in 60 seconds.

1,000 free submissions per month. No credit card. Copy the snippet, paste your access key, watch leads land in your inbox.

Get free access key →Browse all 60 templates →
v0.1 · founders pricing locked in · early access open