Custom-form

A custom form action renders a structured form from a JSON Schema (and optional UI Schema) to collect multiple fields of data from the user in a single step.

This action renders a multi-field form using JSON Forms. The merchant supplies a JSON Schema describing the data shape and validation rules, and an optional UI Schema describing layout. The customer fills out the form and submits the values, which become available in the action state.

Use Cases:

  • Customer Onboarding: Capture first name, last name, email, and address in a single step.
  • Surveys: Render structured surveys with mixed input types (text, number, date, select).
  • KYC / Compliance: Collect a structured set of identity attributes with validation.
  • Custom Metadata: Capture an arbitrary, schema-validated metadata object on the business process.

Supported Capabilities:

  • JSON Schema (schema) validation of submitted data.
  • JSON Forms UI Schema (uischema) for layout customization.
  • Initial form data (data) to pre-populate fields.
  • Customizable submit button label.
  • Read-only and disabled rendering modes.

How to create schema and uischema:

Use these two reference patterns:

  • Normal object form: normalSchema + normalUiSchema
  • Array item form: schema + uischema with items and options.detail

1) Normal object schema pattern:

  • Root must be type: "object"
  • Put required keys in required
  • Define fields in properties with stable ids like #root/<fieldName>
  • Use explicit types: string, number, integer, boolean; for checkbox fields use boolean
  • Use enum for select/dropdown fields (enum values are shown as options)
  • Add validation only where needed: pattern, minimum, maximum

2) Normal object uischema pattern:

  • Use VerticalLayout for standard forms
  • Add one Control per property
  • Use scope #/properties/<fieldName>
  • Use display options like options.multi for multiline text

Normal form example:

{
  "schema": {
    "type": "object",
    "required": ["name"],
    "properties": {
      "name": { "$id": "#root/name", "type": "string", "default": "" },
      "age": { "$id": "#root/age", "type": "integer", "minimum": 18, "default": 18 },
      "category": { "$id": "#root/category", "type": "string", "enum": ["Gold", "Silver", "Lead"], "default": "Silver" },
      "isActive": { "$id": "#root/isActive", "type": "boolean", "default": true }
    }
  },
  "uischema": {
    "type": "VerticalLayout",
    "elements": [
      { "type": "Control", "scope": "#/properties/name", "label": "Name" },
      { "type": "Control", "scope": "#/properties/age", "label": "Age" },
      { "type": "Control", "scope": "#/properties/category", "label": "Category" },
      { "type": "Control", "scope": "#/properties/isActive", "label": "Active" }
    ]
  }
}

Enum note: category renders as a select box and enum values become dropdown options.

3) Array schema pattern:

  • Parent field is type: "array" (example: items)
  • items must be type: "object" when each row has multiple fields
  • Row fields go in items.properties
  • Row required keys go in items.required

4) Array uischema pattern:

  • Add array control scope: #/properties/items
  • Configure row rendering in options.detail
  • Inside detail.elements, keep scopes relative to row, for example #/properties/label
  • Choose HorizontalLayout or VerticalLayout based on row density

Array form example:

{
  "schema": {
    "type": "object",
    "properties": {
      "items": {
        "$id": "#root/items",
        "type": "array",
        "items": {
          "$id": "#root/items/items",
          "type": "object",
          "required": ["label", "quantity"],
          "properties": {
            "label": { "$id": "#root/items/items/label", "type": "string" },
            "quantity": { "$id": "#root/items/items/quantity", "type": "integer", "default": 1 }
          }
        }
      }
    }
  },
  "uischema": {
    "type": "VerticalLayout",
    "elements": [
      {
        "type": "Control",
        "scope": "#/properties/items",
        "options": {
          "detail": {
            "type": "HorizontalLayout",
            "elements": [
              { "type": "Control", "scope": "#/properties/label", "label": "Label" },
              { "type": "Control", "scope": "#/properties/quantity", "label": "Quantity" }
            ]
          }
        }
      }
    ]
  }
}

Quick checklist:

  • Schema property names and uischema scopes must match exactly
  • Keep ids unique and predictable
  • Use enum for select/dropdown fields (enum values are shown as options)
  • Use initialDataFromSchema(schema) for schemaInitialData in stories

Related Actions

This action pairs well with the following actions:

State

The structured form data submitted by the customer, keyed by the property names defined in the JSON Schema.

Union

When unioned there will be no submit button shown in the gui when this action is unionized. Instead, the unionized action will be the trigger to 'save' the current entered values.

Macro level details for how union attribute behaves can be found within the Union documentation.

Features (required)

NameTypeDescriptionRules
namestringName for the form. When set to 'metadata', submitted values are emitted as a metadata event.
Examples: "metadata".
MaxLength: 50
MinLength: 3
Pattern: ^(?!\s*$).+
labelstringLabel rendered above the form.
Examples: "Customer information".
MaxLength: 100
MinLength: 1
Pattern: ^(?!\s*$).+
schemaobject(required) JSON Schema describing the data shape and validation rules for the form.See schema
uischemaobjectJSON Forms UI Schema describing layout and rendering hints for the form.
dataobjectInitial form data used to populate the rendered form.
submitLabelstringLabel for the submit button.
Examples: "Submit".
requiredbooleanWhether form submission is required.
disabledbooleanWhether the form is disabled (non-interactive).
readonlybooleanWhether the form is rendered read-only.

schema

NameTypeDescriptionRules
$idstring
$schemastring
titlestring
descriptionstring
typestring(required) Top-level form schema must be an object.Possible values: object.
requiredarrayNames of properties that must be provided.
propertiesobject(required) Map of field name to its property schema.

Example

Below is an example payload for adding an Custom-form action to a business process or blueprint.

{
    "type": "custom-form",
    "union": 0,
    "features": {
        "schema": {
            "type": "object",
            "required": [
                "name"
            ],
            "properties": {
                "name": {
                    "$id": "#root/name",
                    "type": "string",
                    "default": ""
                },
                "age": {
                    "$id": "#root/age",
                    "type": "integer",
                    "minimum": 18,
                    "default": 18
                },
                "category": {
                    "$id": "#root/category",
                    "type": "string",
                    "enum": [
                        "Gold",
                        "Silver",
                        "Lead"
                    ],
                    "default": "Silver"
                },
                "isActive": {
                    "$id": "#root/isActive",
                    "type": "boolean",
                    "default": true
                }
            }
        },
        "uischema": {
            "type": "VerticalLayout",
            "elements": [
                {
                    "type": "Control",
                    "scope": "#/properties/name",
                    "label": "Name"
                },
                {
                    "type": "Control",
                    "scope": "#/properties/age",
                    "label": "Age"
                },
                {
                    "type": "Control",
                    "scope": "#/properties/category",
                    "label": "Category"
                },
                {
                    "type": "Control",
                    "scope": "#/properties/isActive",
                    "label": "Active"
                }
            ]
        }
    }
}