Skip to content
BetaDocs

Forms

Forms is Typeroll’s built-in core module for collecting structured information from a published site. It covers simple contact forms, booking requests, applications, lead funnels, and other multi-step flows without requiring a third-party form provider.

Forms is included in every Typeroll Cloud site and every self-hosted Typeroll installation. It requires no premium Typeroll App, Extension, or separate subscription. Typeroll Cloud sends submissions to the hosted Forms service; a self-hosted installation sends them to its own Forms endpoint and datastore. The published customer site remains static in both cases.

Cloud plans may set submission-volume, retention, or delivery limits. Those limits do not turn Forms into an optional module. Self-hosted operators control their own capacity, storage, retention, and delivery costs.

  • single-step and multi-step forms;
  • reusable field blocks with client-side and server-side validation;
  • partial saves as a visitor advances through a funnel;
  • a submissions inbox and agent-readable submissions;
  • email notifications through a configured email provider;
  • post-submit actions, success messages, and redirects;
  • module-contributed forms, actions, and prefill sources.

Forms are stored as steps. Each step contains the same block tree used by the rest of Typeroll, so a simple form is one step and a funnel is several steps. The core/form block places a form on a page and wires its signed submission token and runtime automatically.

Published forms submit to Typeroll’s forms service. Each submission is checked with a signed token, a honeypot, server-side validation, and rate limiting. Multi-step forms can save partial answers between steps. Submission data is not embedded in the static site build.

Limits that apply to every submission:

  • the request body may be at most 64 KiB;
  • each answer must be text, a number, a boolean or a list of those, and text is limited to 10,000 characters unless the field sets a higher max;
  • a solved proof of work counts for one accepted post;
  • an email action that sends to an address taken from the answers (for example an autoresponder to {{email}}) reaches the same address at most three times per Site per hour;
  • forms that belong to an app installation cannot be submitted directly.

Self-hosted installs behind a reverse proxy must set AUTH_TRUST_PROXY_HOPS (Compose sets it to 1 behind Caddy); otherwise every visitor shares one rate limit.

In previews (the editor, Preview site, preview links and page previews) forms validate and step through as published but send nothing; a “Preview – nothing is sent” notice marks them. See Forms in previews.

Do not collect sensitive or regulated data unless the site’s legal basis, retention, access controls, and downstream delivery have been reviewed for that specific use case.

Accepted submissions are stored for the site’s submissions inbox. A form can also run configured actions, such as sending an email notification. Email credentials are configured separately under the site’s email settings and stored encrypted.

A submission stores the answers, its status and step, and timestamps. The visitor’s IP address and browser are used in memory for rate limiting only and are not stored. Submissions saved before this change may still hold them: API and MCP responses never return the IP address, and only site administrators see the earlier browser string, shortened to 256 characters.

To remove those earlier values from the datastore, a self-hosted operator on Firestore can run scripts/scrub-submission-network-data.mjs from the Core repository with credentials for the project. It deletes only the fields submitted_from_ip and user_agent from documents in the submissions collection group, in batches, and prints counts. It is a dry run unless you pass --apply --confirm-project with the same project ID:

Terminal window
node scripts/scrub-submission-network-data.mjs --project PROJECT_ID
node scripts/scrub-submission-network-data.mjs --project PROJECT_ID \
--apply --confirm-project PROJECT_ID

Completed submissions are kept until someone deletes them, unless the site opts into a retention period. A site administrator sets it under Forms → Submission retention, or with form_submission_retention_days (30–3650 days) in the site settings. With a period set:

  • each completed submission expires that many days after it was completed, on every form of the site;
  • submissions that are already older are deleted when you save the period, and the rest get an expiry date (expires_at);
  • an expired submission is never listed or returned, and reading the inbox deletes the expired ones it finds;
  • clearing the period (empty, or null) keeps all remaining submissions again.

Partial submissions keep the form’s partial_ttl_days. Questionnaire responses that belong to an app installation are managed by that app.

Each expiring submission also carries a native timestamp, delete_at, so the datastore can delete it without anyone opening the inbox. The operator of the Forms datastore enables this; on a self-hosted installation on Firestore, enable a TTL policy for the collection group submissions on the field delete_at, for example:

Terminal window
gcloud firestore fields ttls update delete_at \
--collection-group=submissions --enable-ttl

Firestore deletes expired documents asynchronously, usually within a day. With a file-backed store, or before the policy is enabled, expired submissions are hidden and deleted when the inbox is read. Webhook delivery records hold only delivery metadata, not answers.

Agents can create, read, update, list, and delete forms. They can also list and delete submissions. With an admin API key they read and set a form’s email notifications and webhooks (actions), exactly as the portal’s Forms editor does; webhook secrets are masked in every response. The chat assistant inside the portal does not manage notifications or read submissions. A caller may provide a flat field list for a simple form; Typeroll converts it to a one-step definition. Multi-step flows use explicit steps.

See Forms tools for the API-oriented reference and Build forms for the task workflow.

The Forms module documents the general form engine. A narrower flow such as a quote request, event registration, lead qualification funnel, or directory edit form should be documented as a recipe or as part of the Extension that ships that form. Customer-specific questions, recipients, legal copy, and routing stay in the site’s configuration rather than this module reference.

form/boolean (or simple field type boolean) renders Yes/No with a clear action. It preserves false, null and omitted answers separately. App-bound forms can declare target.context_params: ["page_id"] to forward explicit URL context keys while hydrating; security context cannot be overridden. Hydration supplies hidden identities and a base revision. Unchanged prefilled answers are omitted from revision-bound submissions. A hidden honeypot stays outside keyboard and assistive-technology interaction. See Owner answers and review for proposal isolation and the separate publication step.

Live choices and actions supplied by installed apps

Section titled “Live choices and actions supplied by installed apps”

Starting with Core 0.2.123 (Extension runtime 0.45.0), an enabled app can contribute Live field options and completion actions. This is available to Cloud and self-hosted installations running the matching Core version.

A Site administrator configures these in the Form editor. Agents first call get_form_capabilities for the selected Site, then read the enabled app’s private instructions through read_app_documentation. Use the returned action/source IDs rather than guessing them. Pass option_sources through create_form or update_form; each source targets an existing select or radio field. App actions receive only answers named in their field_map.

Live choices refresh when the form loads, so adding a choice at the provider does not require another site publication. The server checks choices again before accepting answers. If a source supplies a notice, bind its version to a declared hidden field; the runtime displays the notice next to its choices.

A required app action must succeed before the visitor sees success. If it fails, the form retains the answers and offers a retry of that saved submission. The Forms inbox/API exposes action_status separately from stored-submission status. Ordinary optional notification failures do not undo a successful action.

For the provider protocol, safe retries and the typeroll:form-success browser event, see Forms integration. Never infer success by observing a message’s CSS class. The success event does not itself authorize analytics or advertising tracking; check the visitor’s consent.