Documentation
Getting Started
Collect form submissions from any website in minutes. No backend required — just an API endpoint and a few lines of code.
Paste into Claude, ChatGPT, or any AI assistant to get help integrating.
How it works
inForm gives you an API endpoint for each form you create. Your frontend submits data to that endpoint and inForm handles storage, spam filtering, contact management, and email notifications.
Create a form
Get a unique endpoint URL in the dashboard
Grab your API key
Authenticate submissions from your backend
Submit from your site
POST JSON via a server-side proxy
Manage & get notified
View submissions, configure email alerts
Create a form
Log in to your dashboard and navigate to Forms. Click Create Form and give it a name. The name is for your reference — it shows in notification emails and the dashboard.
Once created, you'll see the form's endpoint URL:
POST https://inform.synergistic.io/api/f/YOUR_FORM_IDGet your API key
Go to Settings in the dashboard to find your API key. It starts with inf_ and authenticates every submission request.
Submit data
Send a POST request with your form data as JSON. Include the API key in the X-API-Key header. Since the key must stay secret, proxy through your own backend.
Backend proxy
export async function POST(request) {
const body = await request.json();
const response = await fetch(
"https://inform.synergistic.io/api/f/YOUR_FORM_ID",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.INFORM_API_KEY,
"X-Forwarded-For": request.headers.get("x-forwarded-for") || "",
"User-Agent": request.headers.get("user-agent") || "",
"Referer": request.headers.get("referer") || "",
},
body: JSON.stringify(body),
}
);
if (!response.ok) {
return Response.json({ error: "Submission failed" }, { status: 502 });
}
return Response.json({ ok: true });
}Frontend form
function ContactForm() {
async function handleSubmit(e) {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target));
await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
}
return (
<form onSubmit={handleSubmit}>
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" />
<input type="hidden" name="_hp" />
<button type="submit">Send</button>
</form>
);
}Spam protection
Every submission runs through built-in spam filtering automatically — no configuration needed. Spam is silently accepted (bots see a success response) but flagged in your dashboard. No notifications are sent for spam.
Honeypot
Add <input type="hidden" name="_hp" /> to your form. Bots fill hidden fields, humans don't.
Gibberish detection
Random character spam in name, company, and identity fields is caught automatically.
URL detection
URLs in name, title, and company fields are flagged.
Spam phrases
Common spam keywords and patterns are filtered.
Timing check
Set a hidden _loadedAt field to Date.now() on page load. Submissions under 3 seconds are flagged as bots.
IP rate limiting
Max 5 submissions per IP address per minute.
Bot user-agent blocking
Known automated tools (curl, wget, python-requests) are rejected.
Email notifications
Set up notification rules on each form to get emailed when submissions arrive.
Admin notifications
Send submission details to your team. Customize which fields appear, their order, and feature important fields with visual accents.
Submitter confirmations
Auto-reply to the person who submitted using their email or businessEmail field.
Field reference
inForm accepts any JSON structure — no predefined schema. Send whatever your form collects. These field names have special behavior:
| Field | Behavior |
|---|---|
email / businessEmail | Submitter confirmations & auto-creating contacts |
name / firstName / fullName | Auto-populates the contact name |
_hp | Honeypot — stripped before storage |
_loadedAt | Page load timestamp for timing check — stripped before storage |
_formId | Optional form variant identifier — stripped before storage |
_turnstileToken | Cloudflare Turnstile CAPTCHA token — required when Turnstile is enabled, stripped before storage |
CORS & allowed origins
By default, submissions are accepted from any origin. To restrict access, add allowed origins in your form settings (e.g. https://yoursite.com). Only requests from listed origins will be accepted.
Contacts
When a submission includes an email or businessEmail field, inForm automatically creates a contact record. Contacts aggregate all submissions from the same email address and can be organized with tags and notes in the dashboard.
CAPTCHA (Turnstile)
For stronger bot protection beyond the built-in heuristics, inForm supports Cloudflare Turnstile, a free, invisible, privacy-friendly CAPTCHA. It runs entirely on the client side — no puzzles for your users.
1. Get Turnstile keys
Go to dash.cloudflare.com and navigate to Turnstile. Create a widget and copy the Site Key (public) and Secret Key (private).
2. Add keys in inForm
Go to Settings in the dashboard and enter both keys in the CAPTCHA (Turnstile) card.
3. Enable per form
Open a form's settings and toggle Require CAPTCHA (Turnstile) on. The toggle only appears after Turnstile keys are configured.
4. Add the widget to your frontend
Load the Turnstile script and render the widget. Pass the resulting token as _turnstileToken in your submission body.
import { useEffect, useRef, useState } from "react";
function ContactForm() {
const [token, setToken] = useState("");
const widgetRef = useRef(null);
useEffect(() => {
// Load Turnstile script once
if (!document.getElementById("cf-turnstile-script")) {
const script = document.createElement("script");
script.id = "cf-turnstile-script";
script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js";
script.async = true;
document.head.appendChild(script);
}
}, []);
async function handleSubmit(e) {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target));
data._turnstileToken = token;
await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
}
return (
<form onSubmit={handleSubmit}>
<input name="name" required />
<input name="email" type="email" required />
<textarea name="message" />
<input type="hidden" name="_hp" />
<div
ref={widgetRef}
className="cf-turnstile"
data-sitekey="YOUR_SITE_KEY"
data-callback="(token) => setToken(token)"
/>
<button type="submit" disabled={!token}>
Send
</button>
</form>
);
}_turnstileToken field is stripped from stored submission data automatically. If Turnstile is enabled and the token is missing or invalid, the submission returns a 403 error.Need help? Contact support
Go to dashboard