Skip to main content

Step Engine Reference

The step engine powers guided, multi-step workflows in Aptean Mesh. Instead of building a separate page for each interaction, a workflow page hosts a step input component that dynamically renders the current step based on JSON metadata returned by Business Central (BC).

Each step is described by a StepMetadata JSON object. The client deserializes this object, selects the correct handler from its HandlerRegistry, and renders the appropriate input or display UI. When the user completes a step, the client submits the value back to BC, which responds with the next step's metadata.

Type Hierarchy

Step metadata uses a sealed type hierarchy with a type discriminator field. The client uses polymorphic Kotlin serialization to resolve the concrete class.

StepMetadata (sealed interface)
├── InputStepMetadata (sealed interface)
│ ├── BarcodeMetadata ("barcode")
│ ├── TextMetadata ("text")
│ ├── NumericMetadata ("numeric")
│ ├── DateMetadata ("date")
│ ├── ChoiceMetadata ("choice")
│ └── ImageMetadata ("image")
└── DisplayStepMetadata (sealed interface)
├── SummaryMetadata ("summary")
└── StatusInfoMetadata ("statusinfo")

The string in parentheses is the JSON type discriminator value (from @SerialName in the Kotlin source).


Base Types

StepMetadata

The root sealed interface. Every step -- input or display -- carries these properties.

PropertyTypeDefaultDescription
typestringrequiredDiscriminator that selects the concrete step type.
idstringrequiredUnique identifier for this step within the workflow.
promptstringrequiredPrimary instruction text displayed to the user.
labelstring?nullOptional secondary label shown above the input area.
acceptsHardwareScanbooleanvariesWhether the step responds to hardware barcode scanner events. Defaults differ per step type.
suggestValuesstring[]?nullOptional list of suggested values shown in an auto-complete overlay.
selectedValuestring?nullCurrently selected value from the suggest-values list (used for UOM selection on numeric steps).

InputStepMetadata

Extends StepMetadata with an input value property. All input step types inherit this.

PropertyTypeDefaultDescription
valuestring?nullPre-populated value to display when the step loads.

All properties from StepMetadata are also present.

DisplayStepMetadata

Extends StepMetadata for read-only steps that present information without collecting input. No additional base properties beyond those in StepMetadata.


Input Step Types

BarcodeMetadata

Type discriminator: "barcode"

A scan-first input that accepts hardware barcode scans with a manual-entry fallback. This is the most common step type in warehouse workflows.

PropertyTypeDefaultDescription
placeholderstring?nullHint text shown in the manual entry field when empty.
minLengthint?nullMinimum acceptable length for manually entered values.
maxLengthint?nullMaximum acceptable length for manually entered values.
manualEntryLabelstring"Manual Entry"Label on the button that toggles manual text input.

Inherited from InputStepMetadata: id, prompt, label, acceptsHardwareScan (default true), value, suggestValues.

{
"type": "barcode",
"id": "scanLocation",
"prompt": "Scan location barcode",
"label": "Location",
"acceptsHardwareScan": true,
"placeholder": "Enter location code",
"minLength": 3,
"maxLength": 20,
"manualEntryLabel": "Type Code"
}

TextMetadata

Type discriminator: "text"

A free-text input field. Supports single-line and multi-line modes.

PropertyTypeDefaultDescription
placeholderstring?nullHint text shown when the field is empty.
minLengthint?nullMinimum character length for validation.
maxLengthint?nullMaximum character length for validation.
multilinebooleanfalseWhen true, renders a multi-line text area instead of a single-line field.

Inherited from InputStepMetadata: id, prompt, label, acceptsHardwareScan (default true), value, suggestValues.

{
"type": "text",
"id": "notes",
"prompt": "Add notes for this receipt",
"label": "Notes",
"placeholder": "Optional notes...",
"multiline": true,
"maxLength": 500,
"acceptsHardwareScan": false
}

NumericMetadata

Type discriminator: "numeric"

A numeric input with optional min/max bounds, increment stepping, and unit-of-measure selection.

PropertyTypeDefaultDescription
placeholderstring?nullHint text shown when the field is empty.
minValueint?nullMinimum accepted value. Validation rejects values below this.
maxValueint?nullMaximum accepted value. Validation rejects values above this.
incrementStepint1Step size for the increment/decrement buttons.
unitstring?nullUnit label displayed next to the value (e.g., "PCS", "KG").

Inherited from InputStepMetadata: id, prompt, label, acceptsHardwareScan (default false), value, suggestValues, selectedValue.

note

NumericMetadata is the only step type that overrides selectedValue from the base interface. When suggestValues contains a list of UOM codes (e.g., ["PCS", "BOX", "PAL"]), selectedValue holds the currently selected UOM. The client renders a UOM selector overlay for this step type.

{
"type": "numeric",
"id": "quantity",
"prompt": "Enter quantity to pick",
"label": "Qty",
"value": "10",
"minValue": 1,
"maxValue": 999,
"incrementStep": 1,
"unit": "PCS",
"suggestValues": ["PCS", "BOX", "PAL"],
"selectedValue": "PCS"
}

DateMetadata

Type discriminator: "date"

A date picker input with optional min/max constraints. Date values are strings in ISO 8601 format (YYYY-MM-DD).

PropertyTypeDefaultDescription
placeholderstring?nullHint text shown when no date is selected.
minDatestring?nullEarliest selectable date (ISO 8601 string).
maxDatestring?nullLatest selectable date (ISO 8601 string).

Inherited from InputStepMetadata: id, prompt, label, acceptsHardwareScan (default false), value.

{
"type": "date",
"id": "expiryDate",
"prompt": "Enter expiry date",
"label": "Expiry",
"placeholder": "Select date",
"minDate": "2025-01-01",
"maxDate": "2030-12-31"
}

ChoiceMetadata

Type discriminator: "choice"

A list of predefined options. Supports single-select and multi-select modes.

PropertyTypeDefaultDescription
choicesStepChoice[][]The list of selectable options. See StepChoice below.
multiSelectbooleanfalseWhen true, the user can select multiple choices.

Inherited from InputStepMetadata: id, prompt, label, acceptsHardwareScan (default false), value.

{
"type": "choice",
"id": "reason",
"prompt": "Select a reason code",
"label": "Reason",
"choices": [
{ "value": "DAMAGED", "label": "Damaged goods" },
{ "value": "SHORT", "label": "Short shipment" },
{ "value": "WRONG", "label": "Wrong item" },
{ "value": "OTHER", "label": "Other" }
],
"multiSelect": false
}

ImageMetadata

Type discriminator: "image"

Captures a photo using the device camera. The captured image is converted to a Base64 string before submission.

No additional properties beyond those inherited from InputStepMetadata.

Inherited: id, prompt, label, acceptsHardwareScan (default false), value.

{
"type": "image",
"id": "damagePhoto",
"prompt": "Take a photo of the damage",
"label": "Photo"
}

Display Step Types

SummaryMetadata

Type discriminator: "summary"

A read-only summary screen that presents a list of label-value pairs. Typically used as a confirmation step before final submission.

PropertyTypeDefaultDescription
summaryItemsSummaryItem[][]List of items to display. See SummaryItem below.

Inherited from StepMetadata: id, prompt, label, acceptsHardwareScan (default false).

{
"type": "summary",
"id": "reviewStep",
"prompt": "Review your entries",
"label": "Summary",
"summaryItems": [
{ "label": "Location", "value": "BIN-A-01" },
{ "label": "Item", "value": "ITEM-1234" },
{ "label": "Quantity", "value": "10 PCS" },
{ "label": "Lot No.", "value": "LOT-2025-001" }
]
}

StatusInfoMetadata

Type discriminator: "statusinfo"

A status banner that communicates the result of an operation. Uses tone and icon to convey severity.

PropertyTypeDefaultDescription
toneToneSUCCESSVisual tone controlling the color scheme. See Tone Enum below.
iconstring?nullMaterial icon name to display. When null, a default icon is derived from the tone value.

Inherited from StepMetadata: id, prompt, label, acceptsHardwareScan (default false).

{
"type": "statusinfo",
"id": "result",
"prompt": "Pick completed successfully",
"tone": "SUCCESS",
"icon": "check_circle"
}

Supporting Models

StepChoice

Used by ChoiceMetadata to define selectable options.

PropertyTypeDescription
valuestringThe value submitted to BC when this option is selected.
labelstringThe display text shown to the user.
{ "value": "DAMAGED", "label": "Damaged goods" }

SummaryItem

Used by SummaryMetadata to define label-value display pairs.

PropertyTypeDescription
labelstringThe label shown on the left side of the row.
valuestringThe value shown on the right side of the row.
{ "label": "Quantity", "value": "10 PCS" }

Tone Enum

The Tone enum controls visual styling for StatusInfoMetadata and other components. It maps to color pairs and default icons.

ValueDescriptionDefault Icon
LOWSubtle, de-emphasized appearanceinfo
MEDIUMStandard, neutral appearanceinfo
HIGHPrimary, emphasized appearanceinfo
ERRORRed tones indicating errors or destructive actionserror
SUCCESSGreen tones indicating success or completioncheck_circle
WARNINGYellow/orange tones indicating cautionwarning
INFOBlue tones for informational contentinfo

Client-Side Architecture

Handler Registry

The client uses a HandlerRegistry to dispatch step metadata to the correct handler. Each step type has a corresponding IStepHandler<M> implementation.

HandlerRegistry
├── BarcodeHandler → BarcodeMetadata
├── TextHandler → TextMetadata
├── NumericHandler → NumericMetadata
├── DateHandler → DateMetadata
├── ChoiceHandler → ChoiceMetadata
├── ImageHandler → ImageMetadata
├── SummaryHandler → SummaryMetadata
└── StatusInfoHandler → StatusInfoMetadata

Each handler implements four methods from the IStepHandler<M> interface:

MethodPurpose
createInitialState(metadata, initialValue)Creates the initial StepInputState from metadata and an optional pre-populated value.
onEvent(event, state, metadata)Processes user interaction events (ValueChanged, ClearError, ClearValue, Submit) and returns a new state.
validate(state, metadata)Returns a validation error message, or null if the current value is valid.
getSubmitPayload(state, metadata)Extracts the value to send back to BC on submission.

StepInputState

The internal state object managed by each handler during a step's lifecycle:

PropertyTypeDefaultDescription
currentValuestring?nullThe user's current input value.
isManualEntryModebooleanfalseWhether the barcode step has toggled to manual text input.
isFocusedbooleanfalseWhether the input field currently has keyboard focus.
isLoadingbooleanfalseWhether an asynchronous operation is in progress.
errorMessagestring?nullValidation error text displayed below the input.

Submission Payload

When the user completes a step, the client sends the value back to BC via the action system. The payload structure is:

{
"value": "<submitted value>",
"barcode": {}
}

For barcode steps where the hardware scanner fires, the barcode object contains the parsed barcode result instead of a manual value. For image steps, the value is a Base64-encoded string of the captured photo.


Mapping to AL Concepts

The step engine JSON schema maps directly to objects and enums in the Business Central AL codebase.

StepType and StepInputType Enums

In BC, each workflow step is defined in the MobileWorkflowStep table. The StepType field determines whether the step collects input or displays information, while StepInputType selects the specific input variant.

JSON typeAL StepTypeAL StepInputTypeDescription
"barcode"InputBarcodeHardware scan with manual fallback
"text"InputTextFree-text entry
"numeric"InputNumericNumber entry with constraints
"date"InputDateDate picker
"choice"InputChoiceSingle or multi-select list
"image"InputImageCamera capture
"summary"Display--Read-only review
"statusinfo"Display--Result banner

MobileWorkflowStep Table

The step metadata JSON is built dynamically by your AL codeunit's IStepHandler implementation (not to be confused with the client-side Kotlin IStepHandler). The AL handler reads step configuration from the MobileWorkflowStep table and serializes it into the JSON format documented above.

Key fields in the table:

FieldMaps To
Step IDid
Promptprompt
Labellabel
Step TypeDetermines InputStepMetadata vs DisplayStepMetadata
Step Input TypeThe type discriminator value
Min Value / Max ValueminValue, maxValue (numeric) or minDate, maxDate (date)
UOM Codeunit (numeric)
ChoicesSerialized into choices array (choice)

IStepHandler (AL Side)

Your AL codeunit implements the step handler interface to build and return step metadata:

// Simplified example -- actual interface varies by workflow
procedure GetCurrentStep(var StepJson: JsonObject)
begin
// Read from MobileWorkflowStep
// Build the JSON metadata object
// Return to the client
end;

procedure ProcessStepResult(StepId: Text; Value: Text; BarcodeData: JsonObject)
begin
// Validate the submitted value
// Advance to the next step or return an error
// Return the next step's metadata
end;

Complete Workflow Example

The following example shows a realistic four-step put-away workflow. Each step is returned one at a time by BC -- the client renders the current step, collects input, submits, and receives the next step in the response.

Step 1 -- Scan the bin barcode

{
"type": "barcode",
"id": "scanBin",
"prompt": "Scan the destination bin",
"label": "Bin",
"acceptsHardwareScan": true,
"placeholder": "Enter bin code",
"minLength": 1,
"maxLength": 20,
"manualEntryLabel": "Type Bin Code"
}

Step 2 -- Enter the quantity

{
"type": "numeric",
"id": "enterQty",
"prompt": "Enter quantity to put away",
"label": "Quantity",
"value": "5",
"minValue": 1,
"maxValue": 100,
"incrementStep": 1,
"unit": "PCS",
"suggestValues": ["PCS", "BOX", "PAL"],
"selectedValue": "PCS"
}

Step 3 -- Select a reason (if partial quantity)

{
"type": "choice",
"id": "reason",
"prompt": "Why is the quantity less than expected?",
"label": "Reason",
"choices": [
{ "value": "DAMAGED", "label": "Damaged on receipt" },
{ "value": "SHORT", "label": "Short delivery" },
{ "value": "MISCOUNT", "label": "Count discrepancy" }
],
"multiSelect": false
}

Step 4 -- Review and confirm

{
"type": "summary",
"id": "review",
"prompt": "Confirm put-away details",
"label": "Review",
"summaryItems": [
{ "label": "Bin", "value": "BIN-A-03-02" },
{ "label": "Item", "value": "WIDGET-500" },
{ "label": "Quantity", "value": "3 PCS" },
{ "label": "Reason", "value": "Short delivery" }
]
}

Result -- Success status

After the user confirms, BC processes the put-away and returns a status step:

{
"type": "statusinfo",
"id": "result",
"prompt": "Put-away completed for BIN-A-03-02",
"tone": "SUCCESS",
"icon": "check_circle"
}