Skip to content

Forms

Forms are the data entry screens the platform shows inside a task, or before an instance is started. Each task in a process can have a different form, with fields and rules specific to that step.

This page covers three things, in this order: filling in a form, building one in the visual builder, and replacing the platform's form with a plugin of your own.

Filling in a form (user's view)

  1. Open a task (under Tasks, in the sidebar)
  2. The task's form is displayed automatically inside the task panel
  3. Fill in the fields as instructed — required fields are marked with *
  4. Click Submit to complete the task; the answers become process variables automatically

TIP

The form version you see is the one that was published when the task was created. Later updates to the form do not affect tasks that are already open.

Visual form builder (admin)

Administrators create and edit forms inside the process definition, under Modeling → Process Catalog → the process → Forms tab, using the drag & drop visual builder. The sidebar carries no Forms item: a form belongs to the process that uses it.

The builder interface

The builder has three panels:

PanelWhat it does
Left — PaletteField types grouped by category; click one to add it to the form
Centre — Canvas / PreviewThe fields in order; drag to reorder; the "Preview" tab shows the real rendering
Right — PropertiesSettings for the selected field

Available field types

GroupTypeDescription
TexttextShort free text
TexttextareaLong, multiline text
TextemailEmail address (validated automatically)
TextphonePhone number
TexturlWeb address (URL)
TextpasswordA credential typed during the task — see Password field, below
NumericnumberNumeric value
NumericcurrencyMonetary value (R$)
NumericpercentagePercentage (%)
NumericsliderA numeric value picked on a track
Date/TimedateDate picker
Date/TimedatetimeDate and time picker
Date/TimetimeTime picker
Date/Timedate-rangeDate range (start and end date)
Documents (BR)cpfCPF (mask applied automatically)
Documents (BR)cnpjCNPJ (mask applied automatically)
Documents (BR)cepPostal code (mask applied automatically)
ChoiceselectDropdown with fixed options
ChoicemultiselectMultiple selection
ChoiceradioSingle choice (radio buttons)
ChoicecheckboxMultiple checkboxes
ChoicebooleanYes / No
ChoiceswitchOn/off as a toggle
SearchlookupPaginated search against an external API (live typing)
FilefileFile upload (configurable: accepted types, multiple)
RatingratingStar rating — 5 by default, configurable up to 10
LayoutheadingSection heading (H2/H3/H4)
LayoutseparatorHorizontal separator
Layoutmd-viewerDisplays Markdown content — collects no data

Password field

The Password type exists for a credential a person types while the process runs. It changes four things compared with an ordinary text field:

  • it renders masked, and the browser does not keep the value in autofill;
  • the value does not appear on screens that show process variables — not on the instance, not in the task's variables, not in search;
  • the value does not reach the AI: an agent on that process receives the field's handle, not its contents;
  • the value is stored encrypted, it stops existing among the instance's variables when the instance ends, and it is wiped from the history seven days later.

The type is the mark

A text field named password gets none of this. The platform does not guess from names — guessing fails in both directions, missing erpCredential and hiding officeWifiPassword. Pick the Password type.

How the value is kept

In one sentence: the password is stored encrypted, it stops existing among the instance's variables when the instance ends, and it is wiped from the history seven days later — a period whoever runs the installation can change. The next step of the process still reads the value normally: a ${erpPassword} in a service task's configuration works exactly as it always did; the only thing that sees the password in the clear is the engine, at the moment it is used.

What the encryption does not settle

The key that encrypts is the installation's own key (APP_ENCRYPTION_KEY). It protects against a leaked database dump, replica or backup — not against someone who already holds the application's key. Guard that key as carefully as you would guard the passwords themselves.

A password is only stored encrypted when the field is of the Password type. If a process step writes that variable again on its own, the value goes back to ordinary text — whoever models the process needs to know this.

The form's body never reaches the log

The platform's request log never writes the body a form submitted, on any route, not even when the submission fails. A validation error still shows in the log with the method, the path, the status and the response that explains the problem — but not what was typed.

Properties every field has

PropertyDescription
LabelText shown above the field
KeyUnique identifier — becomes the process variable name on submit
TypeField type (see the table above)
Width100% / ½ / ⅓ / ¼ — the column it takes in the responsive grid
RequiredMarks the field as mandatory during validation
PlaceholderExample text inside the input
Instruction / HelpDescriptive text shown below the label

Responsive grid

The form uses CSS Grid with up to 4 columns. Each field's Width property defines how many columns it takes:

WidthColumnsResult
fullwhole rowTakes the full width
half2/4Half the row
third1/3A third of the row
quarter1/4A quarter of the row

On small screens (mobile), every field takes the full width regardless of this setting.

Conditional rules

Each field can have one or more conditional rules, configured in the properties panel. A rule defines:

PropertyDescription
ActionWhat happens when the condition is true
Watched fieldWhich other field is monitored
OperatorHow to compare the value (=, , contains, empty, not empty, >, <)
ValueThe value to compare against (not needed for empty / not empty)

Available actions

ActionEffect
Show this fieldThe field becomes visible when the condition is true (hidden by default)
Hide this fieldThe field becomes hidden when the condition is true
Enable this fieldThe field becomes editable when the condition is true
Disable this fieldThe field becomes locked when the condition is true
Make requiredThe field becomes mandatory when the condition is true
Load options from APIOnly for choice fields (select, multiselect, radio, checkbox). Loads the options from a URL, replacing {value} with the watched field's value

WARNING

If a field has a Make required rule, the static "Required" checkbox is disabled — the rules are what decide whether the field is mandatory.

Validations

Beyond being required, each field supports validations specific to its type:

Field typeAvailable validations
Short text (text), Long text (textarea)Custom regex with a message; minimum/maximum number of characters
Email (email), Phone (phone), URL (url)Minimum/maximum number of characters
Number (number), Currency (currency), Percentage (percentage)Minimum and maximum value
Date (date), Date and Time (datetime), Time (time), Date Range (date-range)Minimum and maximum date
CPF, CNPJ, Postal code— fixed format, no extra validations to configure
Select, Rating, File, Search, etc.— no configurable validations

TIP

Fixed-format fields such as CPF (000.000.000-00), CNPJ (00.000.000/0000-00) and postal code (00000-000) already apply their mask automatically. There is no need to configure a regex or a length for them.

Where "required" is actually checked

When a task is completed, required fields are checked on the server too — not only on the screen. Anyone calling the API directly (an integration, a script, an API key) gets a 400 and the task stays open if a required field is missing; before this, a direct call with an empty body completed the task and the next step received blank variables.

The check uses the form version locked when the task was created — the very form the person saw — and follows the conditional rules: a field hidden by a rule is not demanded. Neither is a display-only field (heading, separator, markdown viewer).

What the check demands is presence: a required field has to carry a value. A required field sent with nothing but spaces counts as empty.

What is still browser-only

The start form's variables — for a process and for a case alike — and the format, length and range validations (regex, min/max characters, min and max value, dates) are still checked on the screen only.

Saving a draft skips the check too, and that is deliberate: a draft exists precisely to keep a half-filled form.

Date Range (date-range)

The date-range field shows two date pickers — start and end — side by side. The stored value is an object { start: "YYYY-MM-DD", end: "YYYY-MM-DD" }. Automatic validation guarantees that the start date is earlier than or equal to the end date.

Dates you type do not shift with the time zone

A date you type into a form — a due date, an accounting period, an issue date — is stored exactly as written and looks the same to everyone, wherever they are. A due date of 6 August is 6 August whether you open it from Manaus, Recife or Lisbon.

The same holds for CMS date fields and for process variables of type Date.

This is different from recorded timestamps — when something was created, executed or finished. Those mark a real instant and are shown in the reader's own time zone, because the question there is "what time was it for me when this happened".

Field triggers (automatic fill)

A field can carry triggers: on leaving (blur) or entering (focus) the field, the platform calls a URL and runs a small script that spreads the answer across the other fields. The classic case is a postal code: leave the field, query the address service, fill in street, district and city.

The script gets three things and nothing else:

NameWhat it is
responseThe JSON body the URL answered with
valueThe current value of the field that fired the trigger
set(key, value)Fills in another field of the form

The script runs isolated — and that limits what it can do

The script runs off the page, in a context of its own with no window, no document, no localStorage and no network. It cannot make HTTP calls of its own: the only call is the one to the URL configured on the trigger, made by the platform before the script runs. A script written to use fetch, cookies or any browser API will fail.

On top of that:

  • the script has 2 seconds to finish; past that it is killed — an infinite loop no longer freezes the tab;
  • the trigger URL must be http:// or https://;
  • when something fails — the URL answers an error, the answer is not JSON, the script throws or runs out of time — "Could not run the automatic fill for this field." appears right below the field, and the detail goes to the browser console. Before this, the failure was silent: the field simply stayed empty and nobody was told.

Markdown Viewer (md-viewer)

The ✨ Markdown Viewer field (Layout and Display group in the palette) renders Markdown text inside the form. It is the field to use to show an AI agent's analysis in the task, with headings, lists, bold and tables already rendered.

It collects no data at all — it only displays.

Wiring it to the AI's analysis

The field reads the process variable with the same name as its key. If the agent writes its analysis into the variable aiAnalysis:

  1. Add the Markdown Viewer field to the form
  2. Under Properties, set the Key to aiAnalysis
  3. The Label is the title shown above the box (e.g. "AI analysis")

When the task opens, the variable's content appears rendered. If the variable does not exist yet, the field shows (empty) instead of going blank — a useful sign that the agent has not answered yet.

Display fields are not submitted

Markdown Viewer, Heading and Separator exist only to display, so they are not part of the task submission or the draft save. That keeps the form from writing back over the variable: if the agent updates its analysis while the task is open, the new version is preserved.

Paginated search (Lookup)

The lookup field issues requests as the user types (from 2 characters on). Configure:

PropertyDescription
URLEndpoint containing {search}, which is replaced by the typed text
Label fieldThe property of the JSON response to display as text
Value fieldThe property of the JSON response to store as the value

Example URL: https://api.company.com/customers?q={search}

Form versioning

Every form is versioned automatically. Each save creates a new, immutable version.

Publishing a version

  1. Open the form from the Forms tab of the process definition
  2. Click the 🕑 History tab
  3. Find the version you want and click Publish
  4. The published version shows the ✓ Published tag

The form editor has three tabs — 🎨 Visual builder, </> JSON and 🕑 History — and the Save new version button at the top. Every save creates a new version; publishing is a separate step.

How versions behave in tasks

  • When a task is created in a process, the system automatically locks the published version of the form onto that task
  • If no version was explicitly published, the most recent one is used
  • Future publications do not affect tasks that already exist — a task always shows the form with the schema it had when it was created
  • Submitted answers become process variables

Restoring a version in the editor

In the 🕑 History tab, click Restore to load an older version into the visual builder. This does not change the form — you still have to click Save new version for it to exist.

Standalone forms

Besides forms attached to tasks, the system can hold standalone forms for collecting data or starting processes. They live in the same Forms tab of the definition.

The start form is the one the start-process screen opens before the instance is created. It comes from the Form Key of the start event in a BPMN process, and from the Form Key of the case plan — the outer rectangle of the diagram — in a CMMN case. In both, the form must exist in the definition before the deploy, otherwise the deploy is refused with "Formulário referenciado não existe no ambiente do processo".

Micro-frontend plugins (external forms)

For use cases too complex for the low-code visual builder (heavy 3D rendering, custom advanced drag-and-drop file handling, live socket integrations), Flow.IA supports remote form plugins.

How it works: You build a standalone React/Vite project following our recommended structure (custom-form-boilerplate), build the form component, and serve that URL from a static host (S3, Vercel, Nginx).

The repository carries both halves: samples/custom-form-boilerplate is the plugin, and samples/external-form is a minimal BPMN process that uses one — with the steps to serve the bundle, deploy the definition and start an instance. Running the two together is the quickest check that the whole path is standing.

How to configure it:

  1. Open the BPMN/CMMN modeler.
  2. Open the element's properties: the user task or the process start event, in BPMN; the human task or the case plan, in CMMN.
  3. In the Form Key field (or Start Form Key), enter the plugin URL directly (e.g. http://localhost:3001/assets/plugin.js).
  4. When a user opens a task or process whose formKey starts with http:// or https://, Flow.IA recognises it as an external plugin. It downloads the bundle asynchronously and injects the interface into the screen, passing context variables through props. There is no need to save a local schema in the form builder.

The plugin must build as UMD with React external

The bundle is loaded by a <script> tag and must publish the component on window.CustomFlowiForm. The platform exposes React, ReactDOM, MUI, EmotionReact and EmotionStyled as globals, and the plugin project marks those packages external to use them — two Reacts on one page break hooks.

One specific trap, which the boilerplate is already configured to avoid: the automatic JSX runtime imports from react/jsx-runtime, a different specifier from react, so it escapes the external list. It gets bundled in its CommonJS form, starting with require("react"), and the whole script dies in the browser with require is not defined — with nothing on screen beyond "custom component not found in the script". That is why the boilerplate's vite.config.ts uses jsxRuntime: 'classic' and externalises react by regular expression.

Variables arrive before the form mounts

The platform mounts the plugin only once it holds the process variables, showing a loading indicator in between. This matters because React Hook Form reads defaultValues once, at mount: if the props changed afterwards the form would come up empty and ignore the values that arrived late.

Boilerplate and validation (React Hook Form + Zod)

The recommended starter project (custom-form-boilerplate) comes pre-configured with the industry standard tooling: React Hook Form and Zod. It implements the same logical architecture as Flow.IA's native visual builder, so injecting and extracting data stays predictable:

  • Variable injection (defaultValues): Flow.IA hands the current process variables over through the initialVariables prop. The form injects them natively using React Hook Form's defaultValues.
  • Typed validation (Zod schema): complex form rules are defined in code (z.object({...})), giving full type safety and blocking submission when the data is inconsistent.
  • Partial save and completion (onSave and onComplete): when saving a draft (by calling the injected onSave prop), Zod's strict validation can be skipped, capturing the current data with getValues(). When advancing the task (onComplete), the data is validated against the schema and packaged in exactly the format the process engine expects.

Flowi Agentic — Plataforma de Gestão de Processos com IA