Docs

Add a rock-solid, spam-filtered contact form to any website in under 60 seconds. NormForms safely captures every inquiry, filters bots invisibly, and delivers verified customer leads directly to your inbox and private dashboard. Building a custom client-side integration or webhook receiver? Jump straight to the developer reference.

Getting your form working

There are two simple ways to integrate NormForms depending on your site setup:

Option 1: Embed directly in your website HTML

Create a free account. Enter where customer inquiries should be delivered and the domain your website runs on (you can leave the domain blank while developing). You will receive an endpoint URL formatted like normforms.com/f/YOUR_TOKEN.

Paste this clean HTML snippet into your site, swapping in your form's address for YOUR_TOKEN:

<form action="https://normforms.com/f/YOUR_TOKEN" method="POST">
  <label for="nf-name">Name</label>
  <input id="nf-name" type="text" name="name" autocomplete="name" required>

  <label for="nf-email">Email</label>
  <input id="nf-email" type="email" name="email" autocomplete="email" required>

  <label for="nf-message">Message</label>
  <textarea id="nf-message" name="message" required></textarea>

  <!-- Invisible honeypot traps spam bots silently -->
  <input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off">
  <button type="submit">Send Inquiry</button>
</form>

This snippet is semantic, accessible HTML with zero client-side JavaScript (0kb). Style it using your site's existing CSS or utility classes. Keep the accessible <label> tags and autocomplete attributes so mobile browsers can autofill contact info in one tap.

Never remove the _gotcha field: This is an invisible honeypot. Real visitors will never see or interact with it, but automated spam scripts fill it out compulsively, allowing us to drop them before they hit your inbox.

You don't need to write this by hand. In your dashboard under Embed / Share, you will find your pre-filled snippet ready to copy, along with an AI prompt you can paste directly into Cursor, Claude, v0, or Lovable.

Option 2: Use your standalone hosted page (No code required)

If you don't have a website yet or need a quick way to collect inquiries, every form automatically includes a pre-styled, mobile-friendly hosted page at your endpoint URL: normforms.com/f/YOUR_TOKEN.

You can link to this page from social bio links, Canva websites, email signatures, or print the ready-to-use QR code available in your dashboard. Under Form settings, you can customize the headline, intro copy, badge text, and upload your company logo so the page matches your brand. If you only plan to embed the form on your own site, you can disable the hosted page at any time.

Troubleshooting: What to do if no email arrived

If you sent a test message and didn't receive an email notification, open your form in the dashboard and check the submissions inbox. Whether the submission appears in your dashboard tells you exactly how to fix it:

Case A: The submission is in your dashboard, but no email arrived. Your lead is safe. NormForms always archives every inquiry to database storage before attempting email delivery. If the email didn't reach you, one of these delivery guards is pausing notifications:

Case B: The submission does not appear in your dashboard. If the message is completely missing from your dashboard, it was rejected before storage for one of two safety reasons:

Still have questions? Reach out to [email protected] and a human on our team will help you troubleshoot.

Custom form fields and flexible inputs

NormForms accepts any custom field names without requiring schema configuration. Add inputs like <input name="company">, <input name="budget">, or <input name="phone">, and every value will be formatted cleanly in your notification email and dashboard.

Reserved field names: A few field names are reserved for internal routing and should not be used for customer inputs: token, format, action, controller, and _method. If you need a ticket identifier or order token, name it order_token or ticket_id instead.

Total payload size can be up to 25 KB, which is more than enough for any text contact form. (File attachments are not currently stored; file input values will be noted in your submission data so you can request the document directly from the prospect).

What happens when someone clicks submit?

Sender identity and email deliverability

To ensure reliable inbox placement and comply with strict DMARC, SPF, and DKIM standards, notification emails are sent from our verified mail domain using your form's name: Your Business Name via NormForms.

We recommend naming your form after your business (e.g., "Acme Consulting" instead of "Contact Form"). This ensures your emails are easily searchable in your inbox, and any automated confirmations your prospects receive clearly identify your brand.

Customizing fields on your hosted page

If you use our hosted form page, navigate to Design your form → Hosted page fields in your dashboard to add, edit, or reorder questions:

How NormForms stops spam

Most spam defenses frustrate real human visitors with puzzle sliders and blurry photo grids. NormForms stops bots invisibly before they reach your inbox:

  1. Payload size validation: Drops oversized flood requests immediately.
  2. Domain origin lock: Verifies that submissions originate from your authorized website domain.
  3. Invisible honeypots: Catches automated scripts that blindly populate hidden form fields.
  4. Intelligent rate limiting: Restricts burst volume per IP address and form to prevent flood attacks.
  5. Cloudflare Turnstile (hosted pages): Runs invisible browser telemetry to verify human behavior without interactive puzzles.
  6. Custom blocklists: Automatically checks submissions against your account-wide keyword and email blocklists.

Silent rejection: When an automated bot fails a spam check, NormForms returns a standard success response. Spammers receive no clues about which defense caught them, preventing them from learning how to bypass our filters.

Managing blocklist matches: Any submission flagged by your custom blocklist is diverted to your form's Trash rather than discarded. If an inquiry was caught by mistake, you can restore it with a single click and adjust your blocklist rules.

The domain origin lock and hosted Turnstile

Your site domain lock: Under Spam & security, enter your website hostname (e.g., example.com). NormForms will verify the incoming Origin and Referer headers, automatically allowing the apex domain and any subdomains (like www.example.com). Submissions sent from unauthorized domains are rejected.

Turnstile protection on hosted pages: For forms served on our hosted form page, Cloudflare Turnstile runs invisibly in the background. Because Turnstile requires hostnames to be pre-registered to our Cloudflare widget, Turnstile runs automatically on our hosted page and requires zero setup from you.

For embedded forms on your own site, the combination of our domain origin lock, hidden honeypot, rate limiting, and reputation filtering provides complete protection with 0kb of client-side code and zero user friction.

Local testing without spending quota

You can test your embedded forms thoroughly during local development without burning through your monthly submission limit. Submissions originating from localhost, 127.0.0.1, ::1, or 0.0.0.0 are automatically treated as test submissions:

You can also verify endpoints directly from the command line using curl:

curl -X POST https://normforms.com/f/YOUR_TOKEN \
  -H "Origin: http://localhost:3000" \
  -d "[email protected]&message=Hello+from+local+dev"

Lead retention and data safety

Every inquiry is safely archived in your dashboard:

Handling monthly limits and quotas

The NormForms Promise: We never discard your customer leads. If your website experiences an unexpected traffic spike and exceeds your monthly plan allowance, NormForms continues to accept and store your incoming inquiries safely in your dashboard.

While email notifications pause until you upgrade, your messages are never deleted. When you upgrade your plan, all held inquiries are released immediately. Usage counters reset on the first of each calendar month.

Automated email autoresponders

On paid plans, you can configure an automatic confirmation email sent directly to prospects as soon as they submit your form. Simply provide a subject line and plain-text body in form settings. The autoresponder will trigger whenever the submission includes a valid email field.

Webhooks: Route submissions to your stack

Need to route form data into Slack, a CRM, a database, or Zapier? NormForms can POST every verified submission directly to an HTTPS webhook URL as signed JSON.

Here is an example webhook payload:

{
  "form": { "token": "YOUR_TOKEN", "name": "Contact Sales" },
  "submission": {
    "id": 142,
    "received_at": "2026-09-16T20:30:00Z",
    "source": "embedded",
    "data": { "name": "Jane Doe", "email": "[email protected]", "message": "Requesting a quote" }
  }
}

Each webhook request includes two security headers: X-NormForms-Timestamp and X-NormForms-Signature (an HMAC-SHA256 hex digest of "#{timestamp}.#{raw_body}" signed with your private webhook secret).

# Verifying signature in Ruby
expected = OpenSSL::HMAC.hexdigest("SHA256", secret, "#{timestamp}.#{raw_body}")
is_valid = Rack::Utils.secure_compare(expected, signature) &&
           (Time.now.to_i - timestamp.to_i).abs < 300

Your endpoint must respond with a 2xx status within 10 seconds. If delivery fails or times out, NormForms automatically retries up to four times over six minutes.

Simple, transparent pricing

Free: 50 submissions/month, unlimited forms and projects. Ideal for personal sites and portfolios.
Pro ($7/mo): 200 submissions/month, custom autoresponders, branding removal, priority support.
Growth ($15/mo): 2,000 submissions/month, high-throughput rate limits, signed webhooks.
Business ($50/mo): 20,000 submissions/month, maximum throughput, dedicated support.
Need higher volume? Contact our team.

Framework and site builder guides

Developer reference

Endpoint specifications

Submitting with fetch (AJAX without page reload)

If you are building with React, Next.js, Vue, Svelte, or Astro and prefer client-side submissions without a page reload, save your site domain under Spam & security to enable CORS, then submit via standard fetch:

const response = await fetch("https://normforms.com/f/YOUR_TOKEN", {
  method: "POST",
  headers: { "Content-Type": "application/json", "Accept": "application/json" },
  body: JSON.stringify({
    name: "Jane Doe",
    email: "[email protected]",
    message: "Project inquiry",
    _gotcha: "" // must remain empty
  })
});

const result = await response.json();
if (result.ok) {
  // Show in-page success state
}

Have questions or need technical assistance? Email [email protected].