About YAML syntax for forms
- Forms live at
.bento/form/form.ymlin the workflow’s repository or file set. Only request-type workflows have a form. - The file is parsed with
yaml.safe_loadand validated against a JSON Schema. The schema is the single source of truth for the editor’s IntelliSense, server validation, and AI generation. - Every object in the schema disallows unknown keys. A typo such as
lable:is reported as an error with its line number, rather than being silently ignored. - A form must be valid before a workflow version can be cut. In the editor you can save an invalid draft only after acknowledging the errors.
Removed features
options_loader, validations.validator, and auto_populate with type: inline or type: file have been removed. Bentra never runs user-supplied scripts in a form. Using any of these produces a dedicated error message.Example: a complete form
name
Required. The title displayed at the top of the form.
description
Optional. An explanation shown under the title. If omitted, it defaults to an empty string. Supports Markdown.
Markdown in descriptions
The formdescription and each field’s attributes.description are rendered as Markdown:
- Paragraphs: separate them with a blank line.
- Lists:
-,*or1.items. A list can start on the line right after a paragraph, as on GitHub. - Links:
[text](https://…), and barehttps://URLs are linked automatically. Links open in a new tab. **bold**,*italic*,`inline code`and fenced code blocks.- Headings (
#,##, …) render as small subheadings under the form title.
body
Required. An ordered list of fields. Fields render in the order they are listed.
body[*].type
Required. The kind of field to render.
body[*].id
Required. A unique identifier for the field. The id is used in several places:
- Conditions and dependencies. It is referenced in
visibility.condition,dynamic.depends_on, and templates such as{field_id}. - Workflow inputs. Workflow steps receive the value as an upper-cased environment variable (
service_namebecomes$SERVICE_NAME). See Workflow syntax → Inputs.
body[*].section
Optional. Groups fields under a header. A section header is shown whenever the section value changes between one field and the next, so list the fields of each section consecutively.
body[*].attributes
Optional. Controls how the field looks and behaves. Every key is optional.
body[*].attributes.label
The label displayed for the field. If omitted, a generic label for the field type is used (for example “Text Input” or “Choose an option”).
body[*].attributes.description
Help text displayed under the field’s label. Supports Markdown.
body[*].attributes.placeholder
Placeholder text for input and textarea fields. For input_type: email it defaults to you@example.com.
body[*].attributes.input_type
input fields only. Sets the HTML input type. If the value is missing or unrecognized, a text input is rendered.
body[*].attributes.default
The initial value of the field. The type can be string, number, boolean, list, object, or null, and must suit the field type:
tags: an object of key/value pairs, e.g.{ team: platform }dropdownwithmultiple: true: a list of option values
body[*].attributes.readonly
Boolean. Renders the field as read-only.
body[*].attributes.steppers_only
Boolean. Applies to input_type: number fields only. When true, typing is blocked and the value can only be changed with the spinner arrows.
body[*].attributes.rows
Integer. The number of visible rows in a textarea. Defaults to 4.
body[*].attributes.multiple
Boolean. dropdown only. When true, the field is a multi-select and it submits a list of values.
body[*].attributes.size
Integer. dropdown only. The number of option rows visible in the panel before it scrolls.
body[*].attributes.searchable
Boolean. dropdown only. Adds a search box that filters the options. Defaults to false.
body[*].attributes.options
The choices for dropdown, checkboxes, and radio fields. Each option is an object with these keys:
dropdown, radio, or checkboxes must be one of the listed option values. For cascading dropdowns, the value must be one of the options listed under the parent field’s submitted value in options_map.
body[*].attributes.options_from
dropdown, checkboxes and radio only. Loads the choices from a dataset that a job publishes, instead of listing them in options.
- The form’s workflow needs a read grant on the job (the job’s Data sharing settings). Without one, the version can’t be published.
- A job that hasn’t published the dataset yet is only a warning; the field has no choices until it does.
options_fromcan’t be combined withoptionsordynamic.options_mapon the same field.- With a
"{field_id}"filter, the browser re-filters whenever that field changes, the same wayoptions_mapcascades. The server checks that the submitted value is in a matching row of the current dataset version.
body[*].attributes.key_placeholder
tags only. Placeholder text for the key input. Defaults to key.
body[*].attributes.value_placeholder
tags only. Placeholder text for the value input. Defaults to value.
body[*].attributes.max_pairs
tags only. An integer or null. The maximum number of key/value pairs the user can add in the UI.
body[*].validations
Optional. Rules the value must satisfy. Every key except required accepts null, which means “no limit”. The server skips all validation for a field that is hidden.
body[*].validations.required
Boolean. The field must have a value. Empty strings, empty lists, and empty objects count as missing.
body[*].validations.min_length
Integer. The minimum length of the text.
body[*].validations.max_length
Integer. The maximum length of the text.
body[*].validations.pattern
A regex. The value must contain a match, so anchor the pattern with ^…$ if the whole value must match.
body[*].validations.min
Number. The minimum value for input_type: number.
body[*].validations.max
Number. The maximum value for input_type: number.
body[*].validations.step
Number. The allowed increment for input_type: number.
body[*].validations.min_date
A date string (YYYY-MM-DD). The earliest date allowed for input_type: date.
body[*].validations.max_date
A date string (YYYY-MM-DD). The latest date allowed for input_type: date.
body[*].validations.min_selections
Integer. The minimum number of checked boxes (checkboxes) or key/value pairs (tags).
body[*].validations.max_selections
Integer. The maximum number of checked boxes (checkboxes) or key/value pairs (tags).
body[*].validations.key_pattern
tags only. A regex that every key must match.
body[*].validations.value_pattern
tags only. A regex that every value must match.
Where each validation is enforced
API submissions bypass the browser, so only the checks marked in the Server column apply to them.
body[*].visibility
Optional. Shows the field only when a condition is true.
body[*].visibility.condition
A condition expression evaluated against the current values of the other fields. It is re-evaluated whenever any field changes. When the condition is false:
- the field is hidden and disabled in the browser
- the server skips all of the field’s validations, including
required
A condition that fails to parse is treated as true, so the field is shown. The warning is logged rather than raised.
visibility.depends_on is accepted for backward compatibility but has no effect, because conditions already re-evaluate on every change.body[*].dynamic
Optional. Computes values or options from other fields. All dynamic behavior runs in the browser.
body[*].dynamic.auto_populate
Fills the field automatically. It accepts either a string or a built-in function object.
String forms
The schema also accepts a
values key next to params, but it is ignored. Put the list under params.values.body[*].dynamic.computed
Boolean. Default false.
true: the field is read-only and is overwritten every time it is recalculated.false: the auto-populated value is only a suggestion. It fills the field when the field is empty, or when the current value was itself auto-filled. It never overwrites a value the user typed.
body[*].dynamic.depends_on
A list of field ids. A change to any of them recalculates auto_populate. Changes from text inputs are debounced by 500 ms. The value is also calculated once when the page loads.
body[*].dynamic.options_map
dropdown only. Replaces the field’s options based on the value of a parent field. This is how you build cascading dropdowns. It is a map from each parent value to a list of options.
options_map[parent_value] on a single parent. Nested maps and multi-parent lookups are not supported.
body[*].dynamic.depends_on_field
The id of the parent field for options_map. If omitted, the first entry of dynamic.depends_on is used.
depends_on_fields and lookup_path are accepted by the schema but not implemented.Condition syntax
Conditions are a small, fixed expression language. They cannot run arbitrary code. The browser and the server evaluate them identically.
Precedence, from loosest to tightest:
||, then &&, then !, then == and !=.
Value rules
- An empty value (
"",[],{}) is treated asnull. For example,region != nullmeans “the user picked a region”. - A
checkboxesfield evaluates as the list of checked option values. includestests membership for lists and substring for strings. It isfalsefornull.==and!=compare values as strings, except that booleans compare directly.
Submitted values
This is what each field type submits, as seen inbento_inputs.json and the environment variables your workflow receives.
See Workflow syntax → Inputs and environment variables.