splitforms.com
BEAUTY & WELLNESS · CONTACT FORM TEMPLATE

Spa & Massage Booking Form

Spa bookings split into self-care regulars and gift-card redemptions. Both want different fields, and the form should ask which they are upfront.

1,000/mo free·no card·works on any host
form.htmlhtml51 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 spa booking">
04
05 <label for="name">Full name *</label>
06 <input id="name" type="text" name="name" placeholder="Marie Dubois" required>
07 <label for="email">Email *</label>
08 <input id="email" type="email" name="email" placeholder="marie@example.com" required>
09 <label for="phone">Phone *</label>
10 <input id="phone" type="tel" name="phone" placeholder="+1 555 0142" required>
11 <label for="service">Service *</label>
12 <select id="service" name="service" required>
13 <option value="">Choose…</option>
14 <option>Swedish massage</option>
15 <option>Deep tissue</option>
16 <option>Hot stone</option>
17 <option>Couples massage</option>
18 <option>Facial</option>
19 <option>Body scrub</option>
20 <option>Manicure / pedicure</option>
21 <option>Package</option>
22 </select>
23 <label for="duration">Duration *</label>
24 <select id="duration" name="duration" required>
25 <option value="">Choose…</option>
26 <option>30 min</option>
27 <option>60 min</option>
28 <option>90 min</option>
29 <option>120 min</option>
30 </select>
31 <label for="therapist">Therapist preference</label>
32 <select id="therapist" name="therapist">
33 <option value="">Choose…</option>
34 <option>No preference</option>
35 <option>Female</option>
36 <option>Male</option>
37 </select>
38 <label for="preferred_date">Preferred date *</label>
39 <input id="preferred_date" type="date" name="preferred_date" required>
40 <label for="notes">Allergies or focus areas</label>
41 <textarea id="notes" name="notes" placeholder="Lower back, scent allergies, pregnancy, etc."></textarea>
42
43 <!-- honeypot — bots fill every field -->
44 <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" autocomplete="off">
45
46 <button type="submit">Send</button>
47</form>
48
49<p style="margin-top:12px;font-size:11px;color:#888;text-align:right">
50 Powered by <a href="https://splitforms.com" style="color:#888;text-decoration:none" target="_blank" rel="noopener">splitforms</a>
51</p>
1,000
submissions / mo, free
8
fields, ready to ship
5
code outputs
60s
from copy to inbox
Spa & Massage Booking Form — example splitforms template with submissions inbox
§ 01Why it mattersthe qualifying-fields argument

Spa intake needs more nuance than salon. New clients have intake forms (medical history, allergies, pregnancy, areas to avoid) that legally need to be on file before the first session. Gift-card redemptions need the gift code captured up front so the front desk can pull it. Couples massage needs to confirm two therapists are available simultaneously — a single-therapist confirmation isn't enough. The form sorts these intents before they hit the booking software.

✦ at a glance
  • Spa / massage booking · 8 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 · spa-contact-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 spa booking
Full name
Maya Iyer
Email
maya@studio71.co
Phone
+1 415 555 0142
Service
Swedish massage
Duration
30 min
Therapist preference
No preference
Preferred date
2026-05-15
Allergies or focus areas
Loved your last open house in Hayes — looking for similar with parking. Pre-approved through Wells Fargo.

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 service and duration

Required: service type (Swedish / deep tissue / hot stone / facial / package), duration (60 / 90 / 120 min), preferred therapist if any, gender preference if any.

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

Flag new clients vs returning

Radio for 'first time here?' — if yes, auto-respond with a link to the medical-intake form before the appointment. Saves 10 minutes at the front desk.

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

Gift-card field

Optional gift card code field. Captured leads with codes get a different webhook — front desk pre-pulls the redemption record.

inbox · 1 newjust now
FROM contact@yoursite.com
New spa booking
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.html51 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 spa booking">

  <label for="name">Full name *</label>
  <input id="name" type="text" name="name" placeholder="Marie Dubois" required>
  <label for="email">Email *</label>
  <input id="email" type="email" name="email" placeholder="marie@example.com" required>
  <label for="phone">Phone *</label>
  <input id="phone" type="tel" name="phone" placeholder="+1 555 0142" required>
  <label for="service">Service *</label>
  <select id="service" name="service" required>
    <option value="">Choose…</option>
    <option>Swedish massage</option>
    <option>Deep tissue</option>
    <option>Hot stone</option>
    <option>Couples massage</option>
    <option>Facial</option>
    <option>Body scrub</option>
    <option>Manicure / pedicure</option>
    <option>Package</option>
  </select>
  <label for="duration">Duration *</label>
  <select id="duration" name="duration" required>
    <option value="">Choose…</option>
    <option>30 min</option>
    <option>60 min</option>
    <option>90 min</option>
    <option>120 min</option>
  </select>
  <label for="therapist">Therapist preference</label>
  <select id="therapist" name="therapist">
    <option value="">Choose…</option>
    <option>No preference</option>
    <option>Female</option>
    <option>Male</option>
  </select>
  <label for="preferred_date">Preferred date *</label>
  <input id="preferred_date" type="date" name="preferred_date" required>
  <label for="notes">Allergies or focus areas</label>
  <textarea id="notes" name="notes" placeholder="Lower back, scent allergies, pregnancy, etc."></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.js67 lines
<form id="lf-form">
  <label for="name">Full name *</label>
  <input id="name" type="text" name="name" placeholder="Marie Dubois" required>
  <label for="email">Email *</label>
  <input id="email" type="email" name="email" placeholder="marie@example.com" required>
  <label for="phone">Phone *</label>
  <input id="phone" type="tel" name="phone" placeholder="+1 555 0142" required>
  <label for="service">Service *</label>
  <select id="service" name="service" required>
    <option value="">Choose…</option>
    <option>Swedish massage</option>
    <option>Deep tissue</option>
    <option>Hot stone</option>
    <option>Couples massage</option>
    <option>Facial</option>
    <option>Body scrub</option>
    <option>Manicure / pedicure</option>
    <option>Package</option>
  </select>
  <label for="duration">Duration *</label>
  <select id="duration" name="duration" required>
    <option value="">Choose…</option>
    <option>30 min</option>
    <option>60 min</option>
    <option>90 min</option>
    <option>120 min</option>
  </select>
  <label for="therapist">Therapist preference</label>
  <select id="therapist" name="therapist">
    <option value="">Choose…</option>
    <option>No preference</option>
    <option>Female</option>
    <option>Male</option>
  </select>
  <label for="preferred_date">Preferred date *</label>
  <input id="preferred_date" type="date" name="preferred_date" required>
  <label for="notes">Allergies or focus areas</label>
  <textarea id="notes" name="notes" placeholder="Lower back, scent allergies, pregnancy, etc."></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 spa booking');

    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.tsx82 lines
'use client';

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

export default function SpaBookingForm() {
  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 spa booking');

    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="Marie Dubois" required />
      <label htmlFor="email">Email *</label>
      <input id="email" type="email" name="email" placeholder="marie@example.com" required />
      <label htmlFor="phone">Phone *</label>
      <input id="phone" type="tel" name="phone" placeholder="+1 555 0142" required />
      <label htmlFor="service">Service *</label>
      <select id="service" name="service" required>
        <option value="">Choose…</option>
        <option>Swedish massage</option>
        <option>Deep tissue</option>
        <option>Hot stone</option>
        <option>Couples massage</option>
        <option>Facial</option>
        <option>Body scrub</option>
        <option>Manicure / pedicure</option>
        <option>Package</option>
      </select>
      <label htmlFor="duration">Duration *</label>
      <select id="duration" name="duration" required>
        <option value="">Choose…</option>
        <option>30 min</option>
        <option>60 min</option>
        <option>90 min</option>
        <option>120 min</option>
      </select>
      <label htmlFor="therapist">Therapist preference</label>
      <select id="therapist" name="therapist">
        <option value="">Choose…</option>
        <option>No preference</option>
        <option>Female</option>
        <option>Male</option>
      </select>
      <label htmlFor="preferred_date">Preferred date *</label>
      <input id="preferred_date" type="date" name="preferred_date" required />
      <label htmlFor="notes">Allergies or focus areas</label>
      <textarea id="notes" name="notes" placeholder="Lower back, scent allergies, pregnancy, etc." />

      <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', 'email', 'phone', 'service', 'duration', 'therapist', 'preferred_date', 'notes'];
    $payload = ['access_key' => 'YOUR_ACCESS_KEY'];
    $payload['subject'] = 'New spa booking';

    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.sh12 lines
curl -X POST https://splitforms.com/api/submit \
  -H "Accept: application/json" \
  -d "access_key=YOUR_ACCESS_KEY" \
  -d "subject=New spa booking" \
  -d "name=Jane Builder" \
  -d "email=jane@example.com" \
  -d "phone=+15555555555" \
  -d "service=Swedish massage" \
  -d "duration=30 min" \
  -d "therapist=No preference" \
  -d "preferred_date=2026-05-15" \
  -d "notes=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.

01Do I need a separate medical-intake form?
Yes, legally — most states require it for licensed massage. Use this form for booking; auto-respond with a separate intake link for new clients. Don't try to do both on one form, the medical fields scare regulars off.
02Can the form handle couples massage?
Yes — add a 'couples / single' radio. Couples bookings webhook a flag that tells your scheduler to confirm two therapists for the same slot.
03How does this work with Vagaro / Mindbody?
Webhook the JSON to either platform. Mindbody's Marketplace API takes inbound bookings; Vagaro accepts them via Zapier. Lead arrives with service and therapist preferences pre-filled.
04What about packages and series purchases?
Form captures interest; payment happens via Square / Stripe link in the auto-responder. Don't take payment on the booking form — adds friction and PCI overhead. Capture, confirm, then pay-link.
✻ ✻ ✻

Ship your spa & massage booking form 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