Skip to content

Form submissions

A published form has its own submit endpoint that stores every response and can hand it to a flow. This page covers connecting a form to a flow, the built-in spam and validation guards, and what the submitter gets back.

Store first, then forward

The most important thing to understand about form submissions is the order of operations: a valid submission is always saved first, and only then is it optionally forwarded to a flow. If the flow errors, the response is still safely captured — a published form never silently loses data.

Submissions land in your project's submissions store, tagged with the form name, the workspace, and a timestamp. That happens whether or not a flow is attached.

Connecting a form to a flow

In the form editor, open the Page settings (shown in the Details pane when nothing on the canvas is selected) and use the "On submit → run flow" dropdown. Pick any flow file in the project, or leave it on "None (store only)".

INFO

📸 Screenshot — the Page settings panel with the "On submit → run flow" dropdown expanded.

Under the hood this sets pageSettings.onSubmitFlowFileId on the form. When a submission arrives, the receiver looks up that flow, finds its Start node, and runs it with the submitted fields as inputs — the same way an HTTP trigger would.

TIP

The submitted data is passed to the flow's Start node as-is, keyed by your form fields' name. Give each input a clear, stable name in the designer and your flow can read them directly.

Forwarding is best-effort: if the flow throws, the error is logged, the stored submission is untouched, and the visitor still sees success. The JSON reply includes forwarded: true only when the flow ran without throwing.

Spam protection: the honeypot

Every published form renders a hidden _hp field — positioned off-screen, aria-hidden, and skipped by keyboard navigation, so a real person never sees or fills it. Automated bots that fill every field will fill it too.

When the receiver sees a non-empty _hp, it responds as if the submission succeeded but drops it entirely — nothing is stored, no flow runs. Returning a fake success keeps bots from learning they were caught and adapting. The honeypot field itself is never persisted even on genuine submissions.

Required-field validation

Client-side validation is easy to bypass, so the receiver re-checks it on the server. It collects the name of every field you marked required in the designer and rejects the submission with 400 if any of them is missing, null, or blank — returning the exact list of offending field names.

NOTE

Only fields that are both named and required are enforced. A required field with no name can't be validated (or read by a flow), so always name your inputs.

Guardrails

GuardBehaviour
Rate limit30 submissions per minute per client IP → 429 with Retry-After.
Size capA submission payload over ~100 KB → 413.
Content typeThe body must be JSON shaped as { "data": { … } }, else 400.
Target must be a formThe file ID must resolve to a real form in the project, else 404.

The response

A successful submission returns 201 with:

json
{ "ok": true, "id": "…", "forwarded": true }
  • id — the stored submission's ID.
  • forwarded — whether an onSubmit flow ran successfully.

The published form itself shows a "Thanks — your response has been recorded" confirmation, or a friendly retry message if the request failed.

See also

FlowRunner — the no-code platform for small businesses.