Skip to main content
Enterprise Edition

Overview

A form turns a workflow’s Input schema into a page that workspace members fill in. Each submission starts the workflow with trigger type form. The Input schema supplies each field’s name, type, and default, and a field is required when its input has no default. The form config controls presentation only.

Create a form

A form goes online only when its workflow is published. An online form reads the published Input schema and starts the published workflow definition. Before the first publish, the form reads the draft Input schema so you can prepare it offline. Tracecat checks the config against the published Input schema on every render and submit. When a publish removes or retypes an input the config uses, the form stops accepting submissions until you fix the config.

Form config

Edit the config as YAML on the form’s Config tab. The Preview pane updates as you type, and the editor marks each problem on its line. Tracecat refuses to save a config with errors. Saving replaces the whole config and drops YAML comments. Every key is optional, and Tracecat rejects keys outside this reference. Each layout entry has exactly one of these keys: A field entry also takes widget, options, visible_if, autofill, hidden, accept, and max_files, described in the sections below, and these text keys: Text keys hold at most 255 characters (title, heading, label, placeholder, and an option’s value and label) or 10,000 (description, success_message, help, text, notice, and an option’s description). A layout holds at most 200 entries and options at most 100 choices. This config:
Presents this Input schema:

Widgets

A field without widget uses the default widget for its input type. T | None behaves as T. Input types outside the Default for column, such as dict, duration, any, or str | int, have no default widget. Set widget: user or widget: file on a dict or list[dict] input, or fill the input with autofill or its default. An input with a default and no widget stays off the form and takes its default. The user widget searches active members of the organization. A non-null default must be a valid answer for its field, except on user and file fields, which start empty. Integers in a form stay between -9,007,199,254,740,991 and 9,007,199,254,740,991, the range a browser holds exactly. That covers the default of an int input, hidden or shown, an integer visible_if value, and an answer to an int input, including one sent as text or as a whole-number float. Outside the range, a default or visible_if value is a config error and an answer fails with invalid_value. float inputs are unaffected, since a browser holds every float exactly.

Options and images

options is a list of choices. Each choice has a unique value and optional label, description, and image.
  • For an enum[...] or list[enum[...]] input, options decorate the enum’s values. Each value must be an enum value, and Tracecat appends the values you leave out.
  • For a str or list[str] input, options define the allowed values. select, multi_select, and cards require them.
The cards widget shows each choice as a card with its image, label, and description. An image is an inline data URI: data:image/png;base64,..., data:image/jpeg;base64,..., or data:image/webp;base64,.... Each image is at most 100 KB decoded, and the images in one form total at most 1 MB. Insert image on the Config tab takes a PNG, JPEG, or WebP file, resizes it to 320 px on its long edge, and inserts the data URI at the cursor.

Conditional fields

visible_if shows a field only while another field equals a value, as recipient does in the example. A field takes one rule:
  • The conditional input has a default, which the workflow receives while the field is not shown.
  • The controlling field sits earlier in the form, has no visible_if of its own, and has type str, int, float, bool, or enum[...].
  • equals is a string, number, or boolean the controlling field can take. For a field with options, it is one of their values.
The server re-evaluates every rule on submit and drops values for fields the requester could not see.

Autofill

autofill fills an input on the server when the requester submits. The field stays off the form. Tracecat rejects an autofill input sent in values with unknown_field, so a requester value is always a verified Tracecat user. A service account that submits through the API has no user profile, so it has no requester values, and a user may have no first or last name. An input whose value is missing takes its default. Without a default it holds null if its type takes null, such as str | None, and otherwise the submission fails with required. To record your own end user, add an ordinary input such as end_user_email and send it in values.

Hidden fields

hidden: true keeps an input off the form, and the workflow receives the input’s default, so the input needs one. A field with hidden or autofill takes no other key, and a field can’t be both.

File uploads

The file widget lets requesters attach files. A dict input takes one file. A list[dict] input takes up to max_files files, from 1 to 10 (default 10). accept lists up to 20 extensions such as .pdf, MIME types such as application/pdf, or wildcards such as image/*. Without accept, the field takes every type the workspace allows.
Tracecat accepts a file when the workspace attachment settings allow its extension and MIME type, it matches accept, its content matches its declared type, and it is not empty and at most TRACECAT__MAX_ATTACHMENT_SIZE_BYTES (20 MB by default). One requester holds at most 20 uploaded files on one form before submitting. An uploaded file that no submission claims within 24 hours expires. Tracecat deletes expired files every 10 minutes. Read a file’s content in the workflow with core.forms.download_file, passing ${{ TRIGGER.quote.id }} as file_id. The requester who uploaded a file can download it before and after submitting. A member with form:submission:read can download every submitted file from the Activity tab. Deleting a submission, its form, or the form’s workflow deletes its files.

What requesters see

Requesters need form:read in the form’s workspace and can open every online form in that workspace. After submitting, the requester sees the success_message and a reference such as FRM-8F3C2A1C. A member without form:submit sees the form read-only. An offline form shows This form isn't accepting requests. A member who holds form:read and no workspace:read lands on /forms when they open Tracecat.

What the workflow receives

TRIGGER holds one key per input, validated against the Input schema. For the example form:
An answered field holds its widget’s value. An optional field left empty, a conditional field the requester could not see, and a hidden field hold the input’s default. An autofill input holds the value Tracecat fills in. ENV.workflow.trigger_type is form. The run records the requester as the user who triggered it and executes with the workflow runner’s permissions, as a webhook run does, so a requester needs no workflow or action scopes.

Activity

The Activity tab of a form shows Views and Submissions counts and lists submissions newest first. Expand a submission to read its answers. Each row shows the submitter, the run status, and a Run link. Tracecat counts one view per user per form every five minutes. Tracecat keeps submissions until you delete them. Deleting a submission removes its stored answers and files and leaves the workflow run and its inputs in place. To delete old submissions, run a workflow on a schedule that lists them with core.forms.list_submissions and submitted_before, then deletes each with core.forms.delete_submission. See Forms actions for both steps.

Permissions

Assign Intake only (workspace-intake) to members who only fill in forms. It holds form:read and form:submit and no other workspace access. The Forms sidebar entry and a form’s config need form:update or form:submission:read on top of form:read. See Roles and permissions for assigning roles.

Edit a form in workspace chat

Open the chat panel on a form’s page and describe the change, such as Make model a card picker. Workspace chat knows which form is open, edits it with the core.forms.list_forms, core.forms.get_form, and core.forms.update_form tools, and the editor and preview refresh. The Tracecat MCP server exposes the same operations as list_forms, get_form, and update_form.

Git sync

Git sync stores a form in its workflow’s workflows/<id>/definition.yml as a form: block: the form config followed by status.
A Tracecat instance on a version without Forms writes definition.yml without the form: block, so the next pull on a newer instance takes that form offline and clears its config. Upgrade every instance that pushes to the repository before you build forms in it.

REST API

Build your own frontend on these endpoints. The API Reference tab documents the request and response bodies of every forms endpoint, including the builder endpoints that create, update, and validate a form. Workspace endpoints accept a signed-in user or a service account API key. /intake endpoints accept a signed-in user only. List endpoints take limit (default 20, at most 200) and cursor, and return items with next_cursor. The submissions list also takes submitted_after and submitted_before. Responses return workflow_id as a short wf_... ID, and requests accept either ID form. The forms list returns workflow_id and workflow_title only to callers with form:update or form:submission:read. The users endpoint needs an online form with a user field unless the caller holds form:update.

Render and submit

render returns title, description, success_message, definition_version, config_version, and blocks. A block’s type is heading, text (Markdown), notice (with tone), divider, or field. A field block carries name, label, help, placeholder, widget, required, multiple, default, options, visible_if, and config. A file field’s config holds accept, max_files, and max_file_size in bytes. Hidden and autofill inputs are absent. Show a field with visible_if only while the named field equals equals, and treat a controlling field that is empty or not sent as its default. Send the answers keyed by field name, with the definition_version and config_version from render. Send each value as the workflow receives it (see Widgets), except that a user field takes a user_id from the users endpoint and a file field takes an uploaded file’s id. A field with multiple set to true takes a list.
A 201 response is the receipt: id, reference, submitted_at, and success_message. Without form:submission:read, a caller reads only their own submissions: answers holds the fields the form showed them when they submitted, and definition_version, wf_exec_id, and run_status are null.

Upload and download files

Upload each file before you submit, as multipart/form-data with one file part. Name the file field in the field query parameter. A 201 response is the file object; send its id as the field’s value.
The download endpoint returns download_url, file_name, and content_type. download_url is a short-lived URL that downloads the file as an attachment. The endpoint returns 404 for a file the caller cannot read.

Errors

Submit and upload errors return detail.code and detail.message. A field error’s code is required, unknown_field, invalid_value, invalid_option, invalid_date, invalid_user, invalid_file, or too_many_files. invalid_file means a file ID is not a file you uploaded to that field of this form in the last 24 hours, or a submission already holds it. too_many_files means the field got more files than its max_files.