Declarative business forms: from skill to multi-step flow
Declare fields, steps and submission in a skill so the host can render a business form in chat. Build a three-step travel expense form and follow its configuration, model calls and submitted data.
Outcome: Create and import a three-step business form, then verify drafts, navigation and frozen submission data.
Before you begin: Prepare your own skill directory, create the files below and import it into the app. The chat submission example needs no demo extension, SDK or business server.
On this page
01How the form is defined
The developer defines the business entry point in a skill and specifies fields, validation, steps and submission in YAML. The agent discovers and presents the appropriate template. The host renders it, saves drafts, validates entries and handles user clicks. Confirmed data goes to chat or a business operation as configured. The model does not invent the fields for this business form.
- 01Developer
Skill + YAML define the rules
- 02Agent
Discover and present a template
- 03Host
Render, validate, freeze submission
- 04Your service
Handle business logic via extensions
021. Create your skill
Start with a standalone skill you can import. This example requires neither a demo extension nor a business server. Create the two files below, import the travel-expense directory through skill management and enable it. To ship it in an extension, register its relative directory in extension.json, for example "skills": ["skills/travel-expense"].
travel-expense/
SKILL.md
forms/
travel.yaml032. Register the template in SKILL.md
Save this as SKILL.md. amble-form-ref must be a top-level fenced code block. Its path is relative to the skill directory and cannot escape it or use symlinks. A top-level amble-form block can contain YAML directly instead. Mentioning a filename in prose does not register a template.
---
name: travel-expense
description: Collect travel expenses with a three-step form and prepare an application draft.
---
When the user wants to record travel expenses:
1. Call forms_capabilities and check singleClickChat=1 and readSubmission=1.
2. Call forms_list, find this skill's travel-expense template and use its actual returned key.
3. Call forms_present, optionally prefilling facts already provided in chat. Do not redraw the form in Markdown or submit for the user.
4. Wait for the user submission event, then call forms_read_submission with its submissionId.
5. Use the frozen values to prepare an application draft. Sending to chat does not submit to a finance system.
```amble-form-ref
path: forms/travel.yaml
```043. Define the complete three-step form
Save this YAML as forms/travel.yaml. It collects trip details, expenses and confirmation in order. Intermediate steps omit next, so validation leads directly to the next step. Only the final Send application details button sends to chat. The review step contains instructions and a confirmation checkbox; it does not automatically render a summary of earlier fields.
Try the three-step form
Browser teaching demo of the YAML below. Sample data lives in memory. It calls no host or business service and resets on refresh.
- 1Trip
- 2Expenses
- 3Review
- id: itinerary
title: Trip
fields:
- name: destination
type: text
label: Destination
required: trueThe first two steps update the draft. Confirm the final step to see the frozen data illustration.
Expand the complete YAML · Ready to save
version: 1
id: travel-expense
title: Travel expense registration
description: Review the details and send them to chat for an application draft.
steps:
- id: itinerary
title: Trip
fields:
- name: destination
type: text
label: Destination
required: true
- id: expenses
title: Expenses
fields:
- name: amount
type: money
label: Amount
currency: CNY
scale: 2
minimum: "0.01"
required: true
- name: note
type: textarea
label: Expense note
required: true
maxLength: 500
- id: review
title: Review
description: Return to the previous steps to check your entries before sending.
fields:
- name: confirmed
type: checkbox
label: I have checked my entries
required: true
submit:
kind: chat
label: Send application details
confirmation: onSubmit05Field and step rules
version is 1 and id is the stable template identifier. A single-step form uses root fields, normalized to a step named main. A multi-step form uses steps; do not include root fields as well. step.id and field.name address data, while title and label are displayed. Supported types are text, textarea, number, money, date, select, checkbox, switch and group-toggle; check forms_capabilities for optional features. Money uses decimal strings, dates use valid YYYY-MM-DD values, and selects submit option.value. Limits are 20 steps, 100 fields in total and 128 KiB of YAML.
Read the field and action reference064. Have the agent present the form
Ask “Help me record my travel expenses” in chat. After reading the skill, the agent calls forms_capabilities and forms_list, then forms_present with a returned key. The example below shows forms_present arguments; replace the key and prefill only known business information. Prefill is grouped by step ID. Presentation immediately returns {id,status}; it neither waits for input nor means submission is complete.
{
"key": "REPLACE_WITH_FORMS_LIST_KEY",
"prefill": {
"itinerary": {
"destination": "Shanghai"
},
"expenses": {
"amount": "120.00"
}
}
}075. Follow the data from entry to submission
User edits create host-persisted drafts. Next validates the current visible fields and records that step’s values. Saving or navigating does not send a chat message. On final submission, the host freezes values, initialValues, touched, the template and source identity, then delivers a message with submissionId to the current chat. The agent calls forms_read_submission to read that frozen record and continue the task.
SKILL.md + YAML
→ forms_list: discover registered templates
→ forms_present: create and prefill an instance
→ user edits: drafts → step validation
→ final user submission: frozen data + submissionId
→ current chat: forms_read_submission
→ agent: prepare a draft from confirmed data08Read structured answers
This is a partial excerpt from forms_read_submission, not the full receipt. values contains validated answers by step. initialValues records the supplied creation-time prefill. touched uses stepId.fieldName keys; true means the user edited the field, even if they restored its original value. A missing touched key does not mean the user explicitly disabled anything. deliveryStatus describes delivery, not acceptance by a business system.
{
"values": {
"itinerary": {
"destination": "Shanghai"
},
"expenses": {
"amount": "120.00",
"note": "Local transport"
},
"review": {
"confirmed": true
}
},
"initialValues": {
"itinerary": {
"destination": "Shanghai"
},
"expenses": {
"amount": "120.00"
}
},
"touched": {
"expenses.note": true,
"review.confirmed": true
}
}096. Going back, dependencies and confirmation
Going back retains saved drafts. Editing earlier values invalidates affected step results and required confirmations; it does not undo business operations already performed. dependsOn and visibleWhen reference previously declared stepId.fieldName values. Dependent inputs are retained but require review after upstream changes. Hidden fields retain their draft but are excluded from final answers. Arbitrary expressions and remote dynamic options are not supported. This example uses confirmation: onSubmit, so the final click confirms chat delivery. Omitting it uses an additional confirmation; operation permissions are unaffected.
107. Use operations when connecting a business service
A standalone skill can use chat submission. To save a business draft at an intermediate step or submit to a finance service, an extension must declare and implement its own operations in manifest.operations with page in exposeTo. Use next.kind: operation between steps and submit.kind: operation at the end. input.from supports steps.<step>.<field>, earlier results.<step>.<top-level-result-field> and context.projectId. The fragment below replaces submit; it does not provide an executable backend.
submit:
kind: operation
label: Submit expense
operation: example.expense.submit
input:
destination: {from: steps.itinerary.destination}
amount: {from: steps.expenses.amount}
note: {from: steps.expenses.note}
recovery:
operation: example.expense.lookupSubmission11Write results and recovery
Write operations require a read-only recovery operation in the same extension. The host injects submissionId and other form identity into InvocationContext.form. The business service deduplicates within the real user, tenant and operation scope. requestId is not a substitute for submissionId. Reconcile unknown outcomes before retrying a write. Recovery returns succeeded with result, notExecuted or unknown. Return notExecuted only when the service can prove no execution occurred and the old request cannot later cause an effect.
Implement business operations128. Verify your skill
After importing and enabling the skill, verify that forms_list includes its template. If missing, inspect top-level blocks, reference paths and YAML validation errors. Trigger it in chat and check prefill, required fields and navigation across all three steps. The first two steps must not deliver messages. After submission, check frozen values, decimal-string amounts and submissionId. Restart and inspect saved records. Updating or disabling the skill can invalidate old instances and submissions; present a fresh form. Passing this chat example does not verify business-server writes or arbitrary model behavior.
13Editors and collections use a separate contract
The business forms on this page use amble-form and forms_* tools. Selectable data collections and editors use UI templates and ui_* tools, with different flow receipts and submission data. These contracts are not interchangeable. General follow-up questions use the separate input_* tools and are outside this defined business-form workflow.
Continue to editors and collectionsCheck the result and troubleshoot
Create and import a three-step business form, then verify drafts, navigation and frozen submission data.
Check actual results using this page’s verification steps. For business integrations, inspect UI submission, tool receipts and server records separately.
Open the error reference →