Skip to content

Point any form at one URL.

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.

action https://formflow.digital/api/v1/f/ab12x9
Submission declaration FF-1 · worked example
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

The path of one submission: received, screened, stored, delivered.

received · screened · stored · delivered · held · reasoned · recoverable

your <form> POST /api/v1/f/ab12x9 name · email · message screening per-form model honeypot · turnstile · schema detained counter held · one setting from a lead inbox Jane 0.02 Marco 0.05 Priya 0.01 buy-now-bot 0.98 webhook signed email digest export CSV · XLSX · JSON
real submission — green channel bot — flagged in the inbox, never delivered, never counted refused by your own settings — held, and recoverable

Change one attribute. Ship.

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.

  • 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. 18 field types — text, email, tel, url, number, textarea, select, checkbox, radio, multiselect, date, file, scale, rating, time, duration, grid, checkbox_grid — 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.

<!-- 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>
The same form, in every style FormFlow ships /form-styles/minimal

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>.
Accompanied files contact-form · 2 declared
roof-damage.jpg
image/jpeg · 3.2 MB · verified
accepted
meter-reading.png
image/png · 0.9 MB · verified
accepted
logo.svg refused · SVG not allowed

A filter that learns each form.

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 →

Teach this form: Model updated for this form.
inbox contact-form · ab12x9
Jane Okafor
Loved the demo — can we talk pricing this week?
0.02 · delivered
promo-reply@bulk-seo.link
CHEAP backlinks + guaranteed SEO boost!!!
0.61 · unsure
buy-now-bot
🔥 crypto doubler, act now, limited spots
0.98 · spam
  1. 01

    Honeypot

    An off-screen field bots fill and people never see. Costs nothing, so it runs first.

  2. 02

    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. 03

    Turnstile

    Optional Cloudflare challenge. Fails open on an outage — a real submission never dies with the check.

  4. 04

    Strict schema

    Turn it on and fields your form never declared are rejected at the door. Off by default.

Those four stages are tuned to stop bots. One of them isn't only doing that: your strict schema can't tell a bot from a buyer, and neither can your origin allowlist or your upload rules. Those three reject outright, on your instructions — and the rejection reaches a stranger's browser, never you.

A lead disappears in four places. Only one of them is spam.

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.

  1. Before Send A rule they can't see refuses them, and they give up. Preflight
  2. In transit The browser's own request dies before it lands. Browser outbox
  3. At the door Your own settings turn a real submission away. Detained counter
  4. Over time The form breaks on your site and says nothing. Form health

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 required and type="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 example

Leave the field to see what the endpoint would answer.

form rule · min:20 on message · strict mode on

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-Key the 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.

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 days
dana@meridian.co
Quote for 40 units — my number is 0161 496 0000
refused · phone
t.okonkwo@brightside.dev
Can you invoice us? Reachable on 020 7946 0102
refused · phone
hello@fieldnotes.studio
Interested in the annual plan — call 0117 496 0999
refused · phone

they were told The phone field isn’t accepted by this form.

312 refused this week · 40 accepted

Derived from your own traffic proposal

Accept 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.

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.

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.

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.

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.
Worked example: a declaration's current fields connect through a dark registration rail while its pasted fallback remains underneath.
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.

Worked example: an ochre refused record passes through the current policy gate before reaching the green admitted tray.
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.

Worked example: three different form declarations converge on one green intake and continue as one recorded path.
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.

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.

One inbox, fanned out to everywhere you need it.

Read them in one place — and push the same submission to every destination you've wired up.

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.

acme · dashboard 4 things need you
  • 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
acme · submissions 3,000 included · Starter
Unread Spam Received 1–31 Aug Export CSV · XLSX · JSON
Jane Okafor
Loved the demo — can we talk pricing this week?
0.02
dana@meridian.co
Quote for 40 units — recovered from a refusal
recovered
  • webhookhttps://acme.dev/hooks/formflowdelivered · attempt 2
  • webhookhttps://crm.acme.dev/intake502 Bad Gateway · retrying in 5m
  • digestweekly · 3 recipientsqueued
buy-now-bot
🔥 crypto doubler, act now, limited spots
0.98

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:read and forms: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_unavailable rather than an empty list, so the reason is never a mystery.
GET /api/v1/manage/submissions worked example
# 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…" }
}

Pay for what lands. Not for 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.

Free

$0 forever

Your first form, wired in two minutes.

100 submissions included

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
Multi-step forms
Not included
Submitter receipts
Not included
API reads / minute
Not included
Start free

Starter Lite

$5.99 USD per month

A real plan for a small, steady form.

1,100 submissions included

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
Multi-step forms
Included
Submitter receipts
Included
API reads / minute
60
Choose Starter Lite

Pro

$29.99 USD per month

High volume, larger files.

10,000 submissions included

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
Multi-step forms
Included
Submitter receipts
Included
API reads / minute
300
Choose Pro
Every limit that differs between plans
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
Multi-step forms Not included Included Included Included
Submitter receipts Not included Included Included Included
API reads / minute Not included 60 120 300

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.

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.

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.