Skip to main content
Ticket forms collect structured answers when someone creates a ticket or moves it to certain states. Each form is a JSON Schema plus bindings that copy submitted field values into ticket metadata or tags. Workflows declare when a form is required through validation.on_create and validation.before_transition. Use the Console Forms editor (YAML) or the project form catalog API. Wire forms into workflows on the Workflows page. For HTTP details, see API reference.
Creating or updating forms requires the project permission manage workflows. Reading forms and submitting form_payload on tickets requires project membership (same as other ticket mutations).

What forms do

After validation, the platform merges metadata (dot paths such as wo.location) and inserts tags for bindings with writes_to.kind: tag. Form submission does not replace the whole metadata object—it merges into existing keys.
If the workflow does not require a form for that create or transition, form_payload is ignored (you can still send it; it has no effect).

Catalog forms vs inline

Resolution order: for a given form name, the engine uses the matching entry in forms.extra if present; otherwise it loads the catalog form by name and version. forms.import entries use { name, version } (version optional, same latest-vs-pinned semantics). They document catalog dependencies—especially when you instantiate a workflow template—and should match forms you create in the project before activation.

Form spec fields

Each binding:

JSON Schema validation

The platform validates form_payload with the same JSON Schema compiler used at authoring time (draft 2020-12 and other drafts supported by the schema library). Failed schema validation returns a message like form payload failed validation. A missing bound field returns missing form field "{field}" for form "{name}".

Bindings in practice

Tag bindings run through the same tag validation as POST …/tags (kind registry, optional JSON Schema on values, entity existence checks).

Requiring forms on create

require_form and optional_form cannot both be set on the same workflow; validation rejects that combination at publish time. Use optional_form when automated creators (maintenance plans, alarms auto–work orders) open tickets without human input but operators may still attach intake data when creating manually.

Requiring forms on transition

Add a before_transition rule:
On POST …/transition, if the edge matches and form_payload is empty, the API returns 400 with a message like transition in_progress -> resolved requires form payload. You can combine require_form with require_tag_kinds on the same rule. Tag kinds are checked against tags already on the ticket before the state change; add tags via initial_tags, POST …/tags, or earlier form bindings.

Form versioning

Inline forms in forms.extra are part of the workflow version—they version with the workflow, not the catalog.

Tags and tag kinds

Tags are { kind, value, label? } on the ticket. The API stores kind and value; label is optional display text.

Built-in tag kinds (platform registry)

There is no public API to register new tag kinds—kinds are seeded on the platform. Use only kinds from this registry (plus any added in future migrations). Entity kinds reject missing or out-of-scope targets—for example device not found in this project, user is not a project member in scope, ticket not found in this project. Optional JSON Schema on a kind’s value_schema or tag metadata is enforced when tags are added.

Requiring tag kinds

Tag kinds can come from initial_tags, POST …/tags, or form bindings that write tags.

Example: catalog form

Create a maintenance intake form in the catalog:
Success uses "ok": 1 with data.item containing id, name, and version.

YAML equivalent (Console Forms editor)

Example: workflow requiring the form

Snippet for a maintenance-request workflow (labels new / in_progress / resolved / cancelled):
Publish with POST …/ticket-workflows, activate the version, then create tickets against the workflow def id.

Example: create with form_payload

After success, GET …/tickets/{id} should show metadata.request.summary and tags for wo_type, priority, and eta.

Example: transition with form_payload

Use the label key (resolved), not the display name.

Example: validation errors

Missing required form on create (workflow has require_form, body has no form_payload):
Missing form on transition:
Invalid payload (schema violation, for example wrong enum):
Missing bound field (schema passes but a binding field is absent):
Catalog PATCH creates a new version. Workflows that pin an old require_form.version keep using that snapshot; unpinned rules pick up new fields and enums automatically.

Next steps