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:
- Unverified account email: Click the confirmation link sent to your account email when you signed up. Outbound emails remain paused until your account email is confirmed.
- Unconfirmed destination address: If customer leads are routed to an email address other than your account address, that recipient must click a one-time verification link. Once verified, all queued messages are sent immediately.
- Recipient opt-out: If a notification recipient clicked the unsubscribe link in a previous email, delivery to that address stops. Update the destination address in form settings to resume delivery.
- Monthly submission quota reached: If you exceeded your plan's monthly limit, incoming messages are preserved in your dashboard while email delivery pauses until you upgrade. See handling monthly limits below.
- Blocklist match: If the message matched an entry on your custom blocklist, it was routed directly to your form's Trash. Trash entries do not trigger email notifications and do not count against your quota.
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:
- Domain origin lock mismatch: If you set a production domain (e.g.,
example.com) under Spam & Security settings, submissions from other hosts (such as preview URLs or staging domains) are rejected. Clear the domain setting while testing or test from localhost. - Empty submission or blank required field: Blank submissions are rejected with a
422 Unprocessable Contenterror rather than saved as empty leads.
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?
- Instant confirmation: The visitor is redirected to our clean thank-you page, or to your custom thank-you page if you configured a redirect URL in form settings.
- Direct email notification: You receive an instant email notification containing all submitted fields, with the
Reply-Toheader automatically set to the prospect's email. You can hit reply in your mail app to respond immediately. - Fail-safe dashboard archive: The submission is permanently recorded in your private dashboard with live delivery tracking, ensuring you never lose a lead even if your mail provider experiences downtime.
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:
- Label: The question displayed to your visitor, which also serves as the field name in your dashboard.
- Type: Select from text, email, multi-line textarea, dropdown choices, or a single checkbox.
- Options (for dropdowns): Enter a comma-separated list of choices (e.g.
Under $5k, $5k-$10k, $10k+). - Required: Enforces client-side and server-side validation before the form can be submitted.
- Use the up and down controls to rearrange fields into your preferred order.
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:
- Payload size validation: Drops oversized flood requests immediately.
- Domain origin lock: Verifies that submissions originate from your authorized website domain.
- Invisible honeypots: Catches automated scripts that blindly populate hidden form fields.
- Intelligent rate limiting: Restricts burst volume per IP address and form to prevent flood attacks.
- Cloudflare Turnstile (hosted pages): Runs invisible browser telemetry to verify human behavior without interactive puzzles.
- 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:
- Notifications are delivered to your inbox with a [Test] subject prefix.
- Test submissions consume 0 quota against your monthly plan allowance.
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:
- Retention window: Submissions are kept for 90 days by default, and can be customized per form anywhere from 30 days to forever.
- Soft delete recovery: Submissions you delete manually are held in Recently Deleted for 30 days so you can recover accidental deletions.
- One-click CSV exports: Export your submission history—including held submissions and trash—at any time on all plans, including Free.
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
POST /f/:tokenacceptsapplication/x-www-form-urlencoded,multipart/form-data, orapplication/json. Maximum body size is 25 KB.- HTML browser submissions receive a
303 See Otherredirect to your thank-you page or customredirect_url. Requests sendingAccept: application/jsonreceive a200 OKresponse with{"ok": true}. - Invalid tokens return a
404 Not Found. Oversized payloads return413, rate limits return429, and empty submissions return422. Spam submissions receive a standard200/303success response to prevent feedback to bots.
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].