Forms

Build forms in the dashboard, validate input, and publish versioned snapshots your field users collect against.

Updated 2026-08-16

Forms are built in the dashboard form builder. A form in the builder is a draft — field users never see it. When the form is ready, you publish it, which creates a numbered version for the organization the form belongs to. Devices download published versions; editing the draft changes nothing in the field until you publish again.

Field types

Every field has one of ten data types. Most types offer more than one input style — for example, a number can be typed on a keyboard or counted with a tally button.

TypeWhat it captures
textFree text — single line, multi-line, or barcode scan (QR, Code 128, EAN-13, and more)
numberA number, typed or tallied, with optional min, max, and digit limits
listOne or more choices from a fixed set of options, as a dropdown or toggle buttons
date_timeA date, a time, or both, stored as ISO 8601
gpsA geographic point with accuracy, altitude, and timestamp metadata
mediaA photo, a file upload, a video, or an on-screen signature
lookupOne row picked from a data source
subformA nested group of fields — a single group or a repeating list of rows
noneA heading or description — display only, no data collected
pageA section of the form, grouped behind a navigation bar — layout only, no data collected

Structure

Use none fields to break a form into sections with headings and instructions. Use subform fields when one submission needs repeating rows — line items on an order, multiple assets at one site. Subforms can set a minimum and maximum row count.

A subform's Mobile display setting controls how it appears in the field app. Inline expands its rows in place within the form. Dedicated screen opens each row on its own full screen with its own add and done buttons, which keeps long or complex rows readable on a phone. The setting changes only how the mobile app draws the subform — it never affects field values, formulas, or validation.

You can also choose which of a subform's own fields summarize each row — the fields shown on a collapsed inline row, or on the tappable row that opens a dedicated screen. Pick a name or number so rows are easy to tell apart instead of reading as a plain list.

A form's display fields control how each record is summarized in lists — pick the one or two fields that identify a submission at a glance, such as a site name or a customer name.

Pages

A long form can be split into pages — named sections a field user moves between with a bar of buttons instead of one long scroll. Each button carries a label and an optional icon. Pages are presentational: they change how the form is laid out on screen, not the data it collects. A form with fifty fields across five pages still produces one flat record with fifty answers — nothing is nested.

Building with pages

Add a page from the same menu you add any field. On the canvas a page is a dashed container you drag fields into. It has a Label — the button text — and an optional Icon. Building through the API instead? The keys are label and attributes.icon, and every page needs "interface": "section" — see the field reference. Deleting a page that still holds fields asks whether to delete those fields too or keep them where the page was, so removing a section never quietly loses answers.

A few rules keep pages predictable:

  • A page cannot hold another page.
  • A repeating section can hold pages only when its Mobile display is set to Dedicated screen — the page bar needs the room a dedicated screen gives it.
  • Field names stay unique across the pages at one level, the same as on a form with no pages, because the record stays flat.
  • A page can have a Visible when rule. Hiding a page hides its button and every field on it at once.

Because a page holds no data, changing a field into a page or a page into another type is not offered. Moving fields into or out of a page is a compatible change — see form versions.

Fields left off a page

You do not have to put every field on a page. A field left outside all pages shows on every page, in the order you authored it. A loose field placed between two pages sits after the first page's fields when that page is open and before the second page's fields when the other is open — so it appears to shift as the user moves between pages. To hold a loose field in one spot, author it before the first page (as a header) or after the last page (as a footer).

Choosing an icon

A form icon and a page icon are different settings in different places: the form's is on the form, and a page's belongs to that page. Through the API that is icon at the top level of the form object versus attributes.icon inside the page field — a distinction worth checking, because generalizing the documented form-level key down a level is a reasonable inference and a wrong one.

An icon — for a page button or for the form itself — comes from a searchable library: a short curated list at the top, then the rest of about 7,400 Material Design icons. Type to filter the list by name, or upload your own square image instead. The same picker appears everywhere a form icon can be set.

Every icon in the picker is one drawing from one set, so what you see in the builder is what the crew sees on the phone. If a form arrives with an icon that cannot be drawn — most often a form written through the API with the mdi: prefix left off — the builder says so and suggests the closest real names. It is a warning, not a block: the form still saves, and the icon falls back to a plain document until it is fixed. See the field reference for how an icon value has to be written.

Validation and behavior

For worked, copy-ready examples of formulas and conditional visibility — an "Other" option with a follow-up box, line totals, grand totals, automatic dates — see the builder guides.

  • Required — the field must have a value before the form can be submitted.
  • Limits — number fields take min and max; text fields take a maximum length and a pattern; date fields take earliest and latest bounds.
  • Date picker style — a date field opens either a calendar grid (the default) or a spinner. A calendar is quick for a date near today and slow for one years away, because changing the year means opening a second control. Choose the spinner for birth dates, expiry dates, and historical observations. The choice covers the date only; a time always uses a spinner. Existing forms are unchanged.
  • Conditional visibility — show or hide any field based on the values of other fields, or on who is filling the form. A hidden required field does not block submission.
  • Hidden — never show the field to a field user, while it still holds and submits a value. For ids and keys that feed lookup filters or your own systems.
  • Hide when empty — show the field only once it has a value. For calculated fields that should stay out of the way until they produce something.
  • Default value — the value a new record starts with, which the person filling the form can change. Text, number, choice, and date fields. Applied once when the record is created and never reapplied to an existing one.
  • Default formula — the same, but worked out rather than fixed, and still only once. Use it to capture something about the moment a record was started, such as who started it: a formula would overwrite that every time the record was reopened. A field takes its starting value from one of formula, default formula, or default value — setting two is refused on save.
  • Formulas — compute a field's value from other answers, such as quantity times unit price. Formulas recalculate as the user types.
  • Read-only — display a value the user cannot edit.
  • Display format — number fields can show a set format without changing the stored value: thousands separators, a currency amount with a currency code, or a percent sign. Percent shows the number as typed — 15 shows as 15%. The format applies while filling on mobile, in repeating-section row summaries, and in record views on the dashboard. Rendered documents are not affected — report templates format their own values.

Test mode

The builder's Preview button opens the draft in test mode: a phone-sized frame where the form actually runs. It is not a static mockup — the same rules a device runs, run here as you fill it in.

  • Conditional visibility, formulas, and dependent lookup filters update live as you type, on the same schedule they follow on a device (see form logic).
  • Lookup fields search your organization's real data-source rows, so you can confirm a filtered lookup returns the rows you expect. Very large sources are covered in part: preview searches the first 1,000 rows of the source and shows at most 100 matching rows at a time. A notice tells you when a cap applies.
  • Device-only captures — photo, video, on-screen signature, file upload, GPS, and barcode scan — are simulated with a single tap, since a browser has no camera or location sensor. The field stores a stand-in value so any formula or visibility rule that depends on the field still runs.
  • API request fields store a simulated response instead of sending a real request.
  • Subforms work inline, and a subform set to open on its own screen opens each row in a focused view, matching its Mobile display setting.
  • A form split into pages shows the same page bar here as on a device, so you can click between sections and confirm each page holds the fields you expect. Each page button carries the same count of required fields still empty that a field user sees, which is a quick way to check you have not marked something required by accident.

A collapsible Field values (JSON) panel shows the entry's current state, so you can see exactly what each answer holds as you go.

Nothing entered in test mode is saved or sent — there is no submit button, and the state resets each time you open it. Test mode is the fast way to check logic. To exercise real captures, sync, and a submitted record end to end, publish to a test form and open it in the mobile app.

Publishing and versions

Publishing takes a snapshot of the draft and assigns it the next version number, with an optional changelog note. A form belongs to one organization; within it, users assigned to collect with the form download the new version on their next sync.

Three problems stop a publish outright, whether you publish from the dashboard or through the API:

  • A form whose structure does not match what the platform expects, refused with the problems listed.
  • A circular Visible when rule — see form logic.
  • A Visible when rule or formula that reads a field which is not on the form. Deleting a field does not delete the rules that mention it, so this is what a cleanup usually leaves behind. It matters because the common case hides the field carrying the rule, and a hidden field is not checked for being required — the form would go out quietly missing an answer.

Setting a form ready to test runs the same three checks, since a test form reaches a real device. Saving the draft is never blocked, so you can leave a form half-finished and come back to it — the builder shows the last two as warnings while you work.

If someone saves the draft while your publish is going through, the publish stops and asks you to try again rather than releasing a version whose recorded details describe a different draft. Publish again and it picks up the current draft.

Before the publish goes through, the dialog shows a compatibility preview: whether your edits are compatible with existing records, and if not, which field caused the break. A compatible publish says existing records will appear under the new version; a breaking one says records stay on their versions. What compatible and breaking mean — and what each does to your data — is covered in form versions.

The builder also warns about two edits that look harmless but disconnect data:

  • Deleting a field, then adding one with the same name. Fields are matched by identity, not name, so the new field starts empty — old answers stay with the deleted field.
  • Renaming a choice in a published list. The choice's label is the stored value, so renaming it does not rename old answers.

Both steps are automatable. POST /forms/{id}/ready-for-testing fills the test slot and POST /forms/{id}/publish releases a version, so a scripted setup does not have to choose between publishing untested and stopping for a manual click. See the field reference for the form body those calls take. An API key is not a person, so a form promoted this way grants nobody access automatically — assign collectors and testers yourself.

The first time a form is set ready to test or published from the dashboard, its creator is given full access to it automatically, so you do not have to assign yourself before collecting. Other users still get access through the normal assignment workflow — the builder toolbar's Assign button jumps straight to the form's assignment page, and warns first if the draft has unsaved changes. The assignment settings themselves are described in user management.

flowchart LR D["Draft<br/>(edit freely)"] -->|publish| V["Version 1, 2, 3...<br/>(immutable)"] V -->|assign users| A["Assigned field users"] A -->|sync| M["Devices download"] M -->|capture + submit| R["Records<br/>(tied to that version)"]

Records always remember the version they were submitted against, so a form change never rewrites history — a record submitted against version 3 still reads correctly after version 7 ships. The dashboard lists every published version of a form, and each version's history entry shows what changed since the one before — fields added, removed, and changed, in plain language — with a badge saying whether the change was compatible or breaking. You can review records per version, or across versions at once; see records.

There is no scheduled publishing yet — a publish takes effect as soon as devices sync.

To watch for publishes from your own systems, subscribe to the form.published webhook event — its payload includes a machine-readable list of what changed and whether the change was compatible or breaking.