Invoke forms
A workflow’s invoke form can be the same rich form the escalation surfaces use. Declare inputSchema on the worker config and the Invoke Tool page renders it with the full x-lt-* vocabulary: two-column layout, sections, column groups, conditional display, instruction blocks, dynamic options and a help panel. The submitted data is the x-lt-bind-mapped payload, and the invoke API validates it against the same schema with the same shared pass. A payload the form accepts is a payload the API accepts, and a rejection lists the exact violations the form shows.
Without inputSchema, the page renders the envelope template form from envelopeSchema.
Declare the form
Section titled “Declare the form”const machineToolsConfig: LTWorkerConfig = { description: '**Machine tools**: one form, four tools for one machine.', invocable: true, invocationRoles: ['machine-service', 'maintenance'], envelopeSchema: { data: {}, metadata: { source: 'dashboard' } }, inputSchema: MACHINE_TOOLS_INPUT_SCHEMA, icon: WORKFLOW_ICONS.WRENCH,};Workflow names read as titles on the page (machineTools shows as Machine Tools), and queue names the same way, with the identifiers kept beside them.
| Field | Role in the invoke |
|---|---|
inputSchema |
The form: JSON Schema properties plus x-lt-* tokens. Rendered by the Invoke Tool page; enforced by the invoke API |
envelopeSchema.metadata |
The metadata the Invoke Tool page stamps on every run from this form. envelopeSchema.data is ignored when inputSchema is present |
inputLookups |
Versioned knowledge refs ({ domain, key, version, as? }) the form reads as lookup.<as ?? key>. See Lookups |
invocationRoles |
Who sees the workflow on the Invoke Tool page and who may start it. Empty means every authenticated user |
description |
One line at the top of the form column, markdown allowed. Keep reference material in x-lt-help, which opens in the side panel |
icon |
A curated icon from WORKFLOW_ICONS. Leads the workflow’s row and heading so operators tell tools apart at a glance. The registry offers the same set as a picker |
The registry detail page edits the same schema under Input Form in its Invocation column, beside a live preview, and the registration wizard offers the field for new entries.
Form values, payload and domains
Section titled “Form values, payload and domains”The form edits flat values keyed by property name. On submit, x-lt-bind maps them into the nested data the workflow receives:
// properties"serialNumber": { "x-lt-bind": "machine.serialNumber" },"action": { "x-lt-bind": "tool.action" },"copies": { "x-lt-bind": "tool.details.copies", "x-lt-showIf": "input.action=reprint-label" }
// POST /api/workflows/machineTools/invoke{ "data": { "machine": { "serialNumber": "SN-1042" }, "tool": { "action": "reprint-label", "details": { "copies": 2 } } }, "metadata": { "source": "dashboard" } }Conditions and help tokens read the live form under the input domain: x-lt-showIf: "input.action=retire", {{input.serialNumber}}. resolver names the same values, so a schema written for an escalation form works unchanged. An invoke form reads four domains: input, resolver, metadata (the run’s metadata) and lookup (the editions pinned by inputLookups, keyed <as ?? key>).
The gate validates against the metadata the caller sends. The Invoke Tool page sends the declared envelopeSchema.metadata; an API or MCP caller that wants a condition such as x-lt-showIf: "metadata.mode=strict" to hold sends that metadata itself.
Hidden conditional fields still submit their defaults, as on every x-lt-* form. Treat empty as absent in the workflow.
Conditional instructions
Section titled “Conditional instructions”An instruction is a read-only markdown field. Give it the same x-lt-showIf as the inputs it explains and it arrives with them:
"restartFirst": { "type": "string", "x-lt-widget": "markdown", "readOnly": true, "default": "**Restart first.** Most stalls clear on a restart.", "x-lt-section": "Report the machine offline", "x-lt-showIf": ["input.action=report-offline", "!input.restarted"]}An array of conditions requires every one, so the prompt belongs to its tool and still reacts to the checkbox.
x-lt-help on the schema root is the longer reference for the side panel: markdown, tables and {{input.*}} tokens that re-interpolate as the person types.
From an escalation form
Section titled “From an escalation form”A role’s form_schema can offer the same invoke from inside the work item. An x-lt-invoke field names the workflow and maps the escalation’s values into its payload, then either posts directly or opens this form in a dialog with those values in place. Visibility follows the same per-caller list, the payload meets the same gate, and the run reports over the same events.
The Invoke Tool page
Section titled “The Invoke Tool page”The page is open to anyone the server lists an invocable workflow for. Builders reach Invoke Tool under Orchestrate; every other persona gets a Tools section in the nav that appears when there is something to invoke. Each invocable workflow is a tool. The list sits on the left, grouped by task queue, with the first workflow preselected, and the form takes the rest of the row. Below 1280px the list folds into a select and the form takes the full width.
The form’s side panel has two views: Instructions (the interpolated x-lt-help) and Issues (the current violations, each click focusing its field). A server rejection lands in the same Issues view.
Once a run starts, the page subscribes to system.workflow.{workflowId}.completed and .failed for that id. The outcome and the returned data render beside Submit, so a tool’s answer comes back to the person who asked for it. Submit disarms after one click until the person chooses to submit again, and a warning offers to reconnect live events when they are off.
The server contract
Section titled “The server contract”POST /api/workflows/:type/invoke validates data against input_schema after the role check and before anything starts. A rejection is the canonical 422 body every schema gate uses:
{ "error": "data failed input schema validation (2 violations)", "code": "schema_validation", "violations": [ { "field": "serialNumber", "message": "Required" }, { "field": "action", "message": "Required" } ], "role": null, "schemaVersion": null, "workflowType": "machineTools"}The MCP tools invoke_workflow and invoke_workflow_read_safe run the same gate and return the body as an error result. get_workflow_config returns the input_schema, the declared metadata and the resolved lookup data a caller needs to compose a valid payload. The gate reads bound paths from the nested data, skips fields an x-lt-showIf hides, resolves x-lt-options over pinned lookups, and applies every validation keyword the form applies.
GET /api/workflows/invocable returns the caller’s invocable workflows. The list and the gate share one predicate, so the page offers only workflows the API accepts.
