Quickstart
Five minutes from zero to a spam-protected form. No backend required.
1. Create a form
The easiest way is the dashboard — sign in and click New Form. To create one programmatically, send your Supabase Auth access token as a Bearer token (the owner is taken from the token, not the request body). You'll receive a public access_key (safe to ship in client code) and a private secret_key (for the management API — keep it server-side).
curl -X POST https://api.fieldpost.dev/v1/manage/forms \
-H "Authorization: Bearer YOUR_SUPABASE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Contact"}'2. Add your form
Plain HTML
Point the form at the submit endpoint. The optional embed script (<1KB gz) upgrades it to AJAX, injects the honeypot, and adds inline success/error states. Without it, the form still works via POST + redirect.
<form action="https://api.fieldpost.dev/v1/submit" method="POST" data-fieldpost> <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY"> <input type="text" name="name" required> <input type="email" name="email" required> <textarea name="message" required></textarea> <button type="submit">Send</button> </form> <script src="https://cdn.fieldpost.dev/v1/fieldpost.min.js" defer></script>
React / Next.js
npm install @fieldpost/react
"use client";
import { useFieldpost } from "@fieldpost/react";
export default function ContactForm() {
const { submit, status, error } = useFieldpost({
accessKey: process.env.NEXT_PUBLIC_FIELDPOST_KEY!,
});
return (
<form onSubmit={submit}>
<input name="email" type="email" required />
<textarea name="message" required />
<button disabled={status === "submitting"}>
{status === "submitting" ? "Sending…" : "Send"}
</button>
{status === "success" && <p>Thanks — we'll be in touch!</p>}
{status === "error" && <p>{error}</p>}
</form>
);
}3. Configure security
Every rule runs in block, flag (accept but mark as spam — a dry run), or off. Update per form via the management API:
curl -X PATCH https://api.fieldpost.dev/v1/manage/forms/me \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"security": {
"email_validation": { "mode": "block", "check_mx": true,
"block_disposable": true, "block_role_addresses": false },
"rate_limit": { "mode": "block", "window_seconds": 60, "max": 5 },
"bot_detection": { "mode": "block", "min_fill_seconds": 2 },
"shield": { "mode": "block" },
"captcha": { "provider": "turnstile", "secret": "YOUR_TURNSTILE_SECRET" },
"redact": { "entities": ["credit_card"] },
"allowed_domains": ["yourdomain.com"]
}
}'Special fields
access_key— required; identifies your formredirect— URL for the non-JS thank-you redirectbotcheck— honeypot; leave empty (the embed injects it)email— triggers validation + reply-to + autorespondercf-turnstile-response/g-recaptcha-response/h-captcha-response— CAPTCHA tokens
Reading submissions
curl "https://api.fieldpost.dev/v1/manage/submissions?spam=false&limit=50" \ -H "Authorization: Bearer YOUR_SECRET_KEY"
Webhooks
Add a webhook integration and every accepted submission POSTs to your URL with X-Fieldpost-Timestamp and X-Fieldpost-Signature headers. The signature is HMAC-SHA256(secret, `${timestamp}.${raw_body}`). Verify both the signature and that the timestamp is recent (within ~5 minutes) to reject replays. Webhook URLs must be public HTTPS endpoints — private and loopback addresses are rejected.
{
"event": "submission.created",
"form": { "id": "…", "name": "Contact" },
"submission": {
"id": "…",
"data": { "name": "Ada", "email": "ada@example.com", "message": "…" },
"meta": { "country": "US", "referer": "https://…" },
"created_at": "2026-07-03T12:00:00Z"
}
}