Point any form at one URL.
Keep your own HTML form and change its action to a FormFlow URL. Every submission is screened for spam, stored in one inbox and delivered by signed webhook or email digest — and the real leads your own rules turn away are held for you to recover, not lost.
- 1 Sender
- jane@acme.dev
- 2 Destination
- https://formflow.digital/api/v1/f/ab12x9
- 3 Contents
- name · email · message
- 4 Accompanied files
- 2 images · 4.1 MB
- 5 Screening
- 0.02 · not spam
Change one attribute. Ship.
one attribute · no SDK · no server
Point your form's action at a FormFlow URL and you're done — no SDK, no client library, no server to stand up. Works from a static site, a server, or a one-off curl.
FormFlow receives submissions sent from your own HTML forms to a FormFlow URL, filters them for spam, stores them, and forwards them to destinations you configure (such as your inbox or a webhook endpoint). FormFlow does not host your embedded frontend or decide what data your forms collect; paid plans can also use a FormFlow-hosted page.
<!-- Your existing form. No JS, no SDK. --> <form method="POST" action="https://formflow.digital/api/v1/f/ab12x9"> <input name="email" type="email" required> <textarea name="message"></textarea> <button>Send</button> </form>
# Or POST from anywhere curl -X POST https://formflow.digital/api/v1/f/ab12x9 \ -d "email=jane@acme.dev" \ -d "message=Loved the demo" # → 200 OK · {"success":true,"id":"…"}
{{-- Generated with labels — accessible by default. --}} <form method="POST" action="https://formflow.digital/api/v1/f/ab12x9"> <label for="email">Email</label> <input id="email" name="email" type="email" required> <label for="message">Message</label> <textarea id="message" name="message"></textarea> <button>Send</button> </form>
<!-- Drop into any component. Still no SDK. --> <template> <form method="POST" action="https://formflow.digital/api/v1/f/ab12x9"> <input name="email" type="email" required /> <textarea name="message"></textarea> <button>Send</button> </form> </template>
// Native form action — no client, no state. export default function Contact() { return ( <form method="POST" action="https://formflow.digital/api/v1/f/ab12x9"> <input name="email" type="email" required /> <textarea name="message" /> <button>Send</button> </form> ); }
Or let FormFlow write the fields
- Describe your form — Available on paid plans when enabled. Describe what this form collects, or the test you want to set. You can review the fields before creating the form.
- Import a form schema — Questions spreadsheet · Review the imported fields
Don't hand-write the fields, either. FormFlow infers your form's schema from its own traffic and generates the accessible markup — HTML, Blade, Vue, or React — ready to paste. 19 field types — text, email, tel, url, number, textarea, select, checkbox, radio, multiselect, date, file, scale, rating, time, duration, grid, checkbox_grid, hidden — all supported by the schema. The builder draws the form beside the field list as you edit it, from settings you haven't saved yet. A long form can split into steps, which fall back to one flat page without JavaScript. On the HTML and Blade snippets, switch on Live rendering as you copy and the form follows your field list from then on — the pasted markup stays as the fallback.
It arrives styled. Seven self-contained stylesheets ship with the markup — nothing to install, nothing to link — and a Bootstrap class-map for a site that already loads it. Every one is held to the same budget: labels tied to inputs, 44px targets, a visible focus ring, and contrast verified per style before it ships.
Live rendering Opt in when you copy the native HTML or Blade component. The current field list can follow the Form, while the pasted markup remains the native fallback and the Style remains the choice you copied.
The same form, in every style FormFlow ships
Rendered by the same generator that writes your snippet — no separate marketing copy of it.
The specimen is fetched live from /form-styles/minimal. If it stays blank, that route is unreachable from here — the stylesheet still ships with the snippet either way.
Take file uploads. Skip the storage.
Declare a file field and your form accepts image uploads over plain multipart — nothing to configure. One field takes up to twenty files at once, and you choose which formats it accepts. FormFlow stores them on a private disk and hands the inbox a signed link that expires, so an attachment never sits on a public URL.
- JPEG, PNG, WebP and GIF — SVG is refused, and every file is magic-byte checked, not trusted by extension.
- Per-plan size ceiling, enforced on the way in — 2 MB on Starter Lite, 5 MB on Starter, 25 MB on Pro.
- Stored privately; the inbox shows an expiring signed link, never an inline
<img>.
A filter that learns each form.
bots · flagged, never delivered, never counted
Every form gets its own model. A submission is scored the moment it lands — and every time you correct a verdict, that form gets sharper. The score, the reason, the next action: all stated plainly, with a per-form report of how the model is actually performing. Try it →
-
1
Honeypot
An off-screen field bots fill and people never see. Costs nothing, so it runs first.
-
2
Per-form model
A naïve-Bayes model per form, trained by your corrections. A cold form borrows a team prior, so it flags from submission one.
-
3
Turnstile
Optional Cloudflare challenge. Fails open on an outage — a real submission never dies with the check.
-
4
Strict schema
Turn it on and fields your form never declared are rejected at the door. Off by default.
One inbox, fanned out to everywhere you need it.
received · screened · stored · delivered
Read them in one place — and push the same submission to every destination you've wired up.
- webhookhttps://acme.dev/hooks/formflowdelivered · attempt 2
- webhookhttps://crm.acme.dev/intake502 Bad Gateway · retrying in 5m
- digestweekly · 3 recipientsqueued
Signed webhooks
Every submission POSTed to your endpoint with an HMAC signature you verify. Six attempts, backing off from ten seconds to two hours, every one recorded against the submission. An endpoint that never once succeeds is switched off and its owners emailed the last error — a dead URL stops being retried forever, and you hear about it.
Choose what the body says: FormFlow's own signed JSON, or a message written for a Slack channel to read. The format decides the body and nothing else — the signing, the retries and the backoff are the same either way, and a URL that is itself a credential is never shown back once saved.
Submission digest
One weekly or monthly email to your team, covering every form — not one per submission. Or off, if the inbox and your webhook are enough. Bots never appear in it.
CSV · XLSX · JSON export
Export the whole inbox from the panel. FormFlow builds the file off a queue and emails you a signed link that expires in an hour — spreadsheets are formula-injection safe.
Submitter receipt
An automatic reply to the person who filled the form, sent to an email field you declare on it. Subject and body are yours; their answers appear only where you put a placeholder, escaped as text, and the payload is never copied into the mail. Anyone who opts out stays out, and a per-team ceiling bounds a form that suddenly gets popular. Paid plans.
Everything above lands in one place you can work.
The endpoint is two minutes of your life; this is the part you open on a Monday. It answers one question first — what needs me right now — with a row per condition that is actually true, its reason, and the one link that acts on it. No counters to compare: a number is not an answer. Under that, the inbox is a table, not a dashboard: filter to unread, filter out the bots, pick a date range, export exactly what you filtered to, and open any submission for its full delivery history.
- Approaching quota 2,540 of 3,000 submissions used this period. Review billing
- contact-form Submissions are being refused. Open form
- contact-form — webhook failing 3 failed deliveries in a row, most recently 4 minutes ago. Open form
- 23 unread submissions Open inbox
The same table sits behind the detained ledger, with the reason, the fields at fault, and the exact words the submitter was told — so recovering a lead is reading why it was turned away, then clicking Recover.
And read every one of them back out.
The same submissions, over HTTP, with a token you mint in the panel: the inbox, one submission by id, your forms, and the refused ledger. Cursor-paginated and filterable, so a nightly sync into your own warehouse or CRM asks for what changed rather than for everything, and never needs a browser.
- Read-only by construction. A token carries some of
submissions:read,refusals:readandforms:read— there is no write ability to grant, because there is none. - Shown once when it is minted and stored as a hash, with an optional expiry. FormFlow cannot show it to you again, and neither can anyone who reaches the database.
- Its own per-minute budget, rising with your plan and kept separate from the rate your forms accept submissions at — a busy sync can never spend the room a real lead needs.
- Paid plans. A token on a Free team is answered with
management_api_unavailablerather than an empty list, so the reason is never a mystery.
# Newest first, cursor-paginated. curl https://formflow.digital/api/v1/manage/submissions \ -H "Authorization: Bearer ff_…" { "data": [ { "uuid": "9f1c8a2e…", "form_uuid": "3b7ed004…", "payload": { "email": "jane@acme.dev", "message": "Loved the demo" }, "is_spam": false, "spam_score": 0.02, "created_at": "2026-08-15T09:14:22+00:00" } ], "meta": { "has_more": true, "next_cursor": "eyJpZCI6…" } }
From Form to Result.
The product is the path. Point an existing HTML Form at one URL; FormFlow keeps the handoff, the judgment, the recovery, and the delivery legible.
One public write The Form token is the public write handoff. FormFlow receives the request, judges it, stores the Submission or refusal, and keeps the next delivery decision inside the same recorded path.
Keep the frontend you already own. Then let FormFlow show what happens after Send: fields can follow the current Form, a customer-caused refusal can stay recoverable, and every recipient keeps its own delivery obligation.
- Hosted pages are available on the Starter Lite, Starter, and Pro plans.
- Rosters are available on the Starter Lite, Starter, and Pro plans, with hosted pages.
And the clock is ours, not the browser's.
A Sitting starts when the questions are rendered, and its deadline is stamped on the Seat from the duration in force at that moment. Ingestion compares arrival against that deadline plus a fixed grace it never discloses — the page counts to zero.
- The start is written once. Signing in again, clearing cookies, reloading, a private window — none of them restart it. It runs on wall time, the way an invigilated room already does.
- Shortening the duration while thirty people are sitting shortens nobody's paper; the new one governs Sittings that start after it.
- The paper survives the buzzer. A timed form stops enforcing required fields, so question forty left blank still submits — while undeclared fields are still refused, keeping the schema a contract in the direction that protects you.
- The deadline spends the Seat whether or not anything arrived, so the clock cannot be beaten by reading the questions and coming back tomorrow.
No site to put the form on? Compose a Microsite.
A Microsite is a public page you compose from blocks — hero, features, pricing, FAQ, a form — and publish on FormFlow's hosted origin in one of 5 themes. A form placed on it arrives exactly as one sent from its Hosted page: screened, stored, delivered.
- Build a Microsite free; publishing needs a paid plan.
- Describe the page in a sentence and get a draft of blocks to edit — available on paid plans when enabled.
- Each Microsite is in one language, and a Team can publish as many as it needs.
- If the plan lapses it goes dark with the Team's Hosted pages, rather than serving a page nobody is paying for.
A lead disappears in four places. Only one of them is spam.
held · reasoned · recoverable
The other three happen on your side of the wire, where nothing fails loudly enough for anyone to notice — the submitter thinks they sent it, and no error ever reaches FormFlow. So each one is watched separately, in the order a submitter meets them.
-
Before Send A rule they can't see refuses them, and they give up. Preflight
Ask the endpoint what it would say.
Before the lead is at stake: turn on strict mode and the form FormFlow generates you puts each field past the real judgment before anyone presses Send — same rules, same wording, no consequences. It closes a gap generation can't: a browser enforces
requiredandtype="email", but not the validation rule you set in the builder — so a form whose browser check passes can still be refused by the server. Now it says so while the person can still fix it. What stopped them is counted, too.- Side-effect-free by contract — no row, no quota, no delivery, no spam scoring.
- On the questions it asks — the body's shape, the origin, the schema — it never disagrees with the endpoint it predicts; a test posts the same body to both and compares.
- It asks nothing else. Spam, Turnstile and file fields are left to the real POST, so an accepted preflight means this configuration accepts this, never that it will be stored.
- Draws on the same rate limit as the POST, with a reserve only a real submission can spend.
POST /api/v1/f/ab12x9/preflight worked exampleLeave the field to see what the endpoint would answer.
form rule ·
min:20onmessage· strict mode on -
In transit The browser's own request dies before it lands. Browser outbox
A submission that never arrived can still arrive.
Before it ever reached us: a dropped connection, a tab closed too early — and nobody hears about it, because the submitter believes they sent it and there is no failure for FormFlow to record. Switch on the browser outbox and the attempt is kept and re-sent on the next page load, arriving as an ordinary submission — counted, delivered, and marked as rescued on your dashboard. It ships with the native HTML and Blade snippets you copied with Live rendering on.
- Opt in per form, and never on a form with a file field — an upload can't be rebuilt from local storage.
- Held on the submitter's own device for 24 hours, five waiting at most, and never restored into a form they can see.
- The retry carries an
Idempotency-Keythe endpoint keys a receipt on, so a rescued lead is stored, counted and delivered exactly once. That header is published in the ingestion contract, so your own client can send it too. - A network failure or a 5xx stays queued for the next page load; anything the endpoint actually answered is dropped.
-
At the door Your own settings turn a real submission away. Detained counter
Review and recover
A refused submission isn't dropped — it's held, with the reason and the field to blame. FormFlow then reads both sides of your traffic, the accepted and the refused, and derives the single setting change that wins the most of them back. A change that would cost even one submission you already accepted is discarded, never shown.
Detained · refused submissions contact-form · last 7 daysrefused · phonedana@meridian.coQuote for 40 units — my number is 0161 496 0000refused · phonet.okonkwo@brightside.devCan you invoice us? Reachable on 020 7946 0102refused · phonehello@fieldnotes.studioInterested in the annual plan — call 0117 496 0999they were told
The phone field isn’t accepted by this form.
312 refused this week · 40 accepted
Derived from your own traffic proposalAccept
phone— a field your form sends and your schema never declared.- Wins back
- 312 refused
- Costs
- 0 accepted
Rehearsed against this form's recent traffic — the newest few hundred it accepted, and the ones it turned away.
When FormFlow makes the change itself
A relaxation already proved against the traffic you've accepted can go live without waiting for someone to log in, so a form stops refusing leads while nobody is watching. It stays off until you turn it on, and any change FormFlow makes on its own reverts to the settings you chose unless your traffic confirms it. Silence reverts — a stalled queue, an outage, nobody looking — because the way back is recorded in the same breath as the change.
When one does change, an email says what changed and what it wins back, and links to a page that restores your old settings. Recovered leads are judged again through the live settings, counted, and delivered like any other. Nothing is back-dated.
Recovery Recovery is not a bypass and not a webhook retry. FormFlow judges the stored refusal against the Form's live settings again; only then does it become a real Submission, consume current usage, and enter the same delivery path.
-
Over time The form breaks on your site and says nothing. Form health
And when the form itself goes wrong, nothing tells you either.
Long after you shipped it, your HTML still lives on your site — so a broken or edited form fails silently, and no error ever reaches FormFlow. Health isn't something FormFlow is told; it's computed from the traffic and mailed to you the first time a fault appears.
- Refusal — turning away a real share of its own traffic. Reported instead of a drought, never alongside it.
- Drought — submissions collapse against the form's own baseline.
- Drift — a declared field the traffic has quietly stopped sending.
- Stopped early — the field submitters keep failing, counted while they are still on the page.
Pay for what lands. Not for what bots send.
charged on what lands · not on what bots send
Spam is filtered before it counts. Free includes 100 submissions with a firm cap; paid plans continue into transparent Overage until a spending or safety ceiling is reached. You're emailed at half, four-fifths and all of your included allowance, so a busy month is something you're told about rather than something you find on an invoice.
Estimates are computed in USD; Paddle converts and adds any tax at checkout.
Free
$0 forever
Your first form, wired in two minutes.
100 submissions included
Every limit that differs between plans
- Overage per 1,000
- Firm cap
- Daily ceiling
- 20
- Period ceiling
- 100
- Submissions / minute
- 60
- Stored uploads
- Not included
- Max image upload
- Not included
- Hosted form page
- Not included
- Microsite publishing
- Not included
- Multi-step forms
- Not included
- Submitter receipts
- Not included
- API reads / minute
- Not included
Starter Lite
$5.99 USD per month
A real plan for a small, steady form.
1,100 submissions included
Every limit that differs between plans
- Overage per 1,000
- $7
- Daily ceiling
- 550
- Period ceiling
- 16,500
- Submissions / minute
- 150
- Stored uploads
- 256 MB
- Max image upload
- 2 MB
- Hosted form page
- Included
- Microsite publishing
- Included
- Multi-step forms
- Included
- Submitter receipts
- Included
- API reads / minute
- 60
Starter
$12.99 USD per month
Production forms with real traffic.
3,000 submissions included
Every limit that differs between plans
- Overage per 1,000
- $5
- Daily ceiling
- 1,500
- Period ceiling
- 45,000
- Submissions / minute
- 300
- Stored uploads
- 1 GB
- Max image upload
- 5 MB
- Hosted form page
- Included
- Microsite publishing
- Included
- Multi-step forms
- Included
- Submitter receipts
- Included
- API reads / minute
- 120
Pro
$29.99 USD per month
High volume, larger files.
10,000 submissions included
Every limit that differs between plans
- Overage per 1,000
- $3
- Daily ceiling
- 5,000
- Period ceiling
- 150,000
- Submissions / minute
- 1,200
- Stored uploads
- 10 GB
- Max image upload
- 25 MB
- Hosted form page
- Included
- Microsite publishing
- Included
- Multi-step forms
- Included
- Submitter receipts
- Included
- API reads / minute
- 300
| Free | Starter Lite | Starter | Pro | |
|---|---|---|---|---|
| Included submissions | 100 | 1,100 | 3,000 | 10,000 |
| Overage per 1,000 | Firm cap | $7 | $5 | $3 |
| Daily ceiling | 20 | 550 | 1,500 | 5,000 |
| Period ceiling | 100 | 16,500 | 45,000 | 150,000 |
| Submissions / minute | 60 | 150 | 300 | 1,200 |
| Stored uploads | Not included | 256 MB | 1 GB | 10 GB |
| Max image upload | Not included | 2 MB | 5 MB | 25 MB |
| Hosted form page | Not included | Included | Included | Included |
| Microsite publishing | Not included | Included | Included | Included |
| Multi-step forms | Not included | Included | Included | Included |
| Submitter receipts | Not included | Included | Included | Included |
| API reads / minute | Not included | 60 | 120 | 300 |
| Overage | ||||
| Monthly total |
On every plan, including Free: as many forms as you like, the per-form spam model and its training, refused submissions kept for review, live rendering and browser rescue, signed webhooks, the digest, and CSV · XLSX · JSON export.
Roles, security, retention and billing terms
Also everywhere: Owner & member roles with email invitations, optional two-factor sign-in, a pause switch on any form, a full activity log, and GDPR data retention — email addresses and IPs stored as keyed hashes, purged on schedule. Free resets on the calendar month; paid plans on your billing period. Paid prices are localized by Paddle for your location and may include tax. See our Refund Policy. You can also sign in with Google or GitHub. The panel is in 9 languages, including right-to-left.
Point your first form at FormFlow.
Wire a form in two minutes and watch the inbox fill. Per-team billing, and bots never count against quota. No backend to build, no frontend to host.