Skip to main content
Develop your tool response to return the following types of widgets:
  • Form widgets
    Interactive forms with various input field types including text inputs, text areas, number inputs, date pickers, date range pickers, radio buttons, checkboxes, combo boxes, file uploads, and tables for structured data collection.
    For interactive forms, you can handle data from form submission with the on_event callback. This is how you capture and process user input from form submissions.
  • Custom widgets (user_defined)
    Custom UI components with flexible rendering. This option is supported in embedded chat only.

Forms with interactive widgets

Form widgets provide structured input forms with various field types. You can create forms using either the FormWidget class or the dictionary format with the _meta field.

Widget contract with FormWidget class

The FormWidget class provides a Python-based approach to creating form widgets when using the ToolResult return format.
Import
Import the FormWidget class and input components to create forms in your Python tools:
from ibm_watsonx_orchestrate.run.widgets.forms import (
    FormWidget, TextInput, TextArea, NumberInput, DatePicker,
    DateRangePicker, RadioButton, Checkbox, ToolEvent, MessageEvent
)
Constructor parameters
title
str
required
The form title displayed to users.The following example sets the form title that appears at the top of the form:
title="User Registration Form"
inputs
List[InputWidget]
required
List of input widgets that define the form fields. Supported input types include:
  • TextInput
  • TextArea
  • NumberInput
  • DatePicker
  • DateRangePicker
  • RadioButton
  • Checkbox
The following example defines a list of input widgets for the form, including a required email field and an optional comments field:
inputs=[
    TextInput(
        name="email",
        title="Email Address",
        required=True
    ),
    TextArea(
        name="comments",
        title="Comments",
        placeholder="Enter your feedback"
    )
]
For more details on each input widget, see Additional features of Python tools: Creating a form tool.
description
str
Optional description text displayed below the form title.The following example adds a description that appears below the form title to provide additional context:
description="Please complete all required fields to submit your request."
submit_text
str
Custom text for the submit button. Defaults to “Submit”.The following example customizes the submit button text:
submit_text="Register Now"
cancel_text
str
Custom text for the cancel button. Defaults to “Cancel”.The following example customizes the cancel button text:
cancel_text="Go Back"
on_event
List[Union[ToolEvent, MessageEvent]]
List of event handlers that define actions when the form is submitted. For more information, see Handling data from form submission.These examples show how to configure event handlers for form submission using either ToolEvent or MessageEvent:
# Example for ToolEvent
on_event=[
    ToolEvent(
        tool="process_form",
        parameters={"email": "", "name": ""},
        map_input_to="submit"
    )
]

# Example for MessageEvent
on_event=[
    MessageEvent(
        message="Thank you for completing the survey! Your feedback helps us improve.",
        map_input_to="submit"
    )
]

Widget contract with dictionary format

When using the dictionary return format, widget contracts are defined in the _meta["com.ibm.orchestrate/widget"] field.
_meta
Dict[str, Any]
Additional metadata for custom extensions or context. Widget contracts in _meta["com.ibm.orchestrate/widget"] require the following fields:
FieldTypeDescription
response_typestringMust be "forms" for form widgets.
json_schemaobjectJSON Schema defining form fields and validation rules. For more information, see JSON Schema details.
ui_schemaobjectUI schema specifying widget types per field. For more information, see UI Schema details.
form_dataobjectPre-populated default values for form fields. For more information, see Form Data details.
on_eventarrayActions triggered on form submission. For more information, see Handling data from form submission.
The following example shows the basic structure of a tool response with a form widget using the dictionary format:
return {
    "content": [
        {
            "type": "text",
            "annotations": {
                "audience": ["user"]
            },
            "text": "Message text"
        }
    ],
    "_meta": {
        "com.ibm.orchestrate/widget": {
            "response_type": "forms",
            "json_schema": {...},
            "ui_schema": {...},
            "form_data": {...},
            "on_event": [...]
        }
    }
}
JSON Schema details
The json_schema field defines the structure, validation rules, and metadata for form fields. Implement the following properties to determine what data the form collects and how it must be validated:
PropertyTypeDescription
titlestringThe form title displayed to users
descriptionstringOptional description text displayed below the form title
typestringMust be "object" for form schemas
requiredarrayArray of field names that are required
propertiesobjectObject containing field definitions. For more information, see properties definition.
properties definition
Each field in the properties object can include:
PropertyTypeDescription
typestringData type: "string", "integer", "number", "boolean", "array", "object".
titlestringLabel displayed for the field.
descriptionstringHelp text or description for the field.
defaultanyDefault value for the field.
enumarrayArray of allowed values for selection widgets.
enumNamesarrayHuman-readable labels for enum values.
formatstringFormat specification, for example, "date" for date fields.
minimumnumberMinimum value for numeric fields.
maximumnumberMaximum value for numeric fields.
minItemsnumberMinimum number of items for array fields.
maxItemsnumberMaximum number of items for array fields.
oneOfarrayArray of schema options for conditional validation.
The following example demonstrates a complete JSON Schema for a user registration form with required fields, validation rules, and data types:
{
  "title": "User Registration",
  "description": "Please complete all required fields",
  "type": "object",
  "required": ["firstName", "email"],
  "properties": {
    "firstName": {
      "type": "string",
      "title": "First Name"
    },
    "email": {
      "type": "string",
      "title": "Email Address",
      "format": "email"
    },
    "age": {
      "type": "integer",
      "title": "Age",
      "minimum": 18,
      "maximum": 120
    }
  }
}
UI schema details
The ui_schema field specifies how form renders widget type and appearance for each field. You can define the following properties:
PropertyTypeDescription
ui:widgetstringWidget type to render.
ui:placeholderstringPlaceholder text for input fields.
ui:optionsobjectAdditional widget-specific options.
ui:enumNamesarrayCustom labels for enum values for combobox widget.
The ui_schema and json_schema work together to define form fields. They reference the same field names as keys:
  • JSON schema (json_schema.properties.fieldName) defines the data structure, type, validation rules, and metadata for a field.
  • UI schema (ui_schema.fieldName) specifies how that same field must be rendered as a widget.
For example, if you define a field called "firstName" in your JSON Schema properties, you must use the same key "firstName" in your UI Schema to specify its widget type. The field name acts as the connection point between the two schemas.The following example shows how the same field name ("firstName") is used consistently across all three schema objects:
{
  "json_schema": {
    "properties": {
      "firstName": {           // Field name: "firstName"
        "type": "string",
        "title": "First Name"
      }
    }
  },
  "ui_schema": {
    "firstName": {             // Same field name: "firstName"
      "ui:widget": "TextWidget"
    }
  },
  "form_data": {
    "firstName": "John"        // Same field name: "firstName"
  }
}
All three objects (json_schema.properties, ui_schema, and form_data) use the same field name as the key to reference the same form field.
Form data details
The form_data field provides pre-populated default values for form fields. These values appear in the form when it’s first displayed to the user, providing sensible defaults to guide users and pre-populating fields with known user data when available. Consider the following implementation details for form data:
  • Field names in form_data must match the field names defined in json_schema.properties.
  • Values must match the data type specified in the JSON schema.
  • Optional fields can be omitted or set to empty values. Use empty strings ("") for optional text fields.
  • For array fields such as date ranges or tables, provide an array of values.
  • For date fields, use ISO 8601 format (YYYY-MM-DD).
  • For selection widgets, ensure default values match one of the enum options.
  • For file upload fields, provide an object with fileName and url properties.
The following example demonstrates pre-populated form data with various field types including text, numbers, dates, and file uploads:
{
  "firstName": "John",
  "email": "john@example.com",
  "age": 25,
  "dateRange": ["2025-09-12", "2025-09-25"],
  "accountNumber": "231",
  "comments": "",
  "uploadedFile": {
    "fileName": "document.pdf",
    "url": "https://example.com/files/document.pdf"
  }
}

Handling data from form submission

The on_event defines actions triggered when users interact with the form. This is how you capture and process user input from form submissions:

on_event parameters

type
string
required
The action type to perform. Implement the code based on the format:
  • If you use the dictionary format, set the type value to either "tool" or "message".
  • If you use the FormWidget() class, you must call either ToolEvent() or MessageEvent() to perform the action.
tool
string
The name of the tool to invoke. Required when type is "tool".
parameters
object
Key-value pairs mapping form field names to their values. Required when type is "tool". Values are populated automatically from form data.
map_input_to
string
required
The event trigger. Use "submit" to trigger the action when the form is submitted.
message
string
The message to send to the agent. Required when type is "message".

Tool call

The ToolEvent() or type: "tool" actions invoke another tool with form data as parameters when the form is submitted. This is the most common approach for processing form submissions. You can use this approach when you need to process form data with custom logic, validate inputs, make API calls, or perform complex operations. The tool specified must accept parameters matching the form field names. When the user submits the form, this tool is automatically called with the form data.

Using ToolEvent() with FormWidget

When using the FormWidget class, configure on_event by using the ToolEvent class.
ToolEvent class
The ToolEvent class defines a tool invocation event that triggers when a form is submitted. Use this class with FormWidget to process form data through another tool.
Import
Import the ToolEvent class to define tool invocation events:
from ibm_watsonx_orchestrate.run.widgets.forms import ToolEvent
Constructor parameters
tool
str
required
The name of the tool to invoke when the event is triggered. This tool must be defined in your agent and accept parameters matching the form field names.The following example specifies the tool to invoke when the form is submitted:
tool="process_registration"
parameters
Dict[str, str]
required
Dictionary mapping form field names to their parameter names in the target tool. Keys must match the name attributes of form inputs. Values are typically empty strings that get populated with form data on submission.The following example maps form field names to tool parameters that will be populated with form data:
parameters={
    "firstName": "",
    "email": "",
    "age": ""
}
map_input_to
str
required
The event trigger type. Use "submit" to trigger the action when the form is submitted.The following example sets the event trigger to activate when the form is submitted:
map_input_to="submit"
The following example shows how to create a ToolEvent instance for processing form submissions:
from ibm_watsonx_orchestrate.run.widgets.forms import ToolEvent

# Define a tool event that processes form submission
event = ToolEvent(
    tool="process_user_data",
    parameters={
        "name": "",
        "email": "",
        "phone": ""
    },
    map_input_to="submit"
)
This complete example demonstrates a contact form with ToolEvent handling:
from ibm_watsonx_orchestrate.agent_builder.tools import tool
from ibm_watsonx_orchestrate.run.widgets.forms import FormWidget, TextInput, ToolEvent
from ibm_watsonx_orchestrate.run.tool_result import ToolResult, TextContent, Annotations, Role

@tool()
def show_contact_form():
    """Display contact information form."""
    form = FormWidget(
        title="Contact Information",
        inputs=[
            TextInput(
                name="name",
                title="Full Name",
                required=True
            ),
            TextInput(
                name="email",
                title="Email",
                required=True
            ),
            TextInput(
                name="phone",
                title="Phone Number",
                required=False
            )
        ],
        on_event=[
            ToolEvent(
                tool="save_contact",
                parameters={
                    "name": "",
                    "email": "",
                    "phone": ""
                },
                map_input_to="submit"
            )
        ]
    )
    
    return ToolResult(
        content=[
            TextContent(
                text="Please provide your contact information",
                annotations=Annotations(
                    audience=[Role.USER]
                )
            )
        ],
        widget=form
    )

@tool()
def save_contact(name: str, email: str, phone: str = ""):
    """Save contact information."""
    # Process the contact data
    return ToolResult(
        content=[
            TextContent(
                text=f"Contact saved: {name} ({email})",
                annotations=Annotations(
                    audience=[Role.USER]
                )
            )
        ]
    )

Using type: "tool" with dictionary format

When using the dictionary return format, configure on_event within the _meta field: The following example shows the basic structure of a tool event configuration in dictionary format:
"on_event": [
    {
        "type": "tool",
        "tool": "process_registration",
        "parameters": {
            "firstName": "",
            "email": ""
        },
        "map_input_to": "submit"
    }
]
This complete example demonstrates a two-tool workflow where the first tool displays a registration form and the second tool processes the submitted data:
@tool()
def show_registration_form():
    """Display registration form."""
    return {
        "content": [
            {
                "type": "text",
                "annotations": {
                    "audience": ["user"]
                },
                "text": "Please complete the registration form"
            }
        ],
        "_meta": {
            "com.ibm.orchestrate/widget": {
                "response_type": "forms",
                "json_schema": {
                    "title": "User Registration",
                    "type": "object",
                    "properties": {
                        "firstName": {"type": "string", "title": "First Name"},
                        "email": {"type": "string", "title": "Email"}
                    }
                },
                "ui_schema": {
                    "firstName": {"ui:widget": "TextWidget"},
                    "email": {"ui:widget": "TextWidget"}
                },
                "form_data": {"firstName": "", "email": ""},
                "on_event": [
                    {
                        "type": "tool",
                        "tool": "process_registration",  # Name of the tool to call when the event is triggered.
                        "parameters": {
                            # Each key must match a parameter name in process_registration(...).
                            # With map_input_to set to "submit", these fields are populated from the form submission.
                            "firstName": "",
                            "email": ""
                        },
                        "map_input_to": "submit"  # Maps submitted form values to the parameters above.
                    }
                ]
            }
        },
        "structuredContent": None,
        "context_updates": {}
    }

@tool()
def process_registration(firstName: str, email: str):
    """Process the registration form submission."""
    return {
        "content": [
            {
                "type": "text",
                "annotations": {
                    "audience": ["user"]
                },
                "text": f"Registration successful! Welcome, {firstName}. Confirmation sent to {email}."
            }
        ],
        "_meta": {},
        "structuredContent": None,
        "context_updates": {}
    }

Message call

The MessageEvent() or type: "message" actions send a predefined message to the agent instead of calling a tool. The agent processes this message as if the user had typed it. You can use this approach when you want to trigger a conversational flow or simple acknowledgment without custom processing logic.

Using MessageEvent() with FormWidget

The MessageEvent class defines a message event that sends a predefined message to the agent when a form is submitted. Use this class with FormWidget to trigger conversational flows without custom processing logic.
Import
Import the MessageEvent class to define message events:
from ibm_watsonx_orchestrate.run.widgets.forms import MessageEvent
Constructor parameters
message
str
required
The message to send to the agent when the event is triggered. The agent processes this message as if the user had typed it.The following example defines the message that will be sent to the agent when the form is submitted:
message="Thank you for your submission. Your request has been received."
map_input_to
str
required
The event trigger type. Use "submit" to trigger the action when the form is submitted.The following example sets the event trigger to activate when the form is submitted:
map_input_to="submit"
Example
The following example demonstrates a survey form using MessageEvent to send a thank you message upon submission:
from ibm_watsonx_orchestrate.agent_builder.tools import tool
from ibm_watsonx_orchestrate.run.widgets.forms import FormWidget, TextArea, RadioButton, MessageEvent
from ibm_watsonx_orchestrate.run.tool_result import ToolResult, TextContent, Annotations, Role

@tool()
def show_survey_form():
    """Display a simple survey form."""
    form = FormWidget(
        title="Quick Survey",
        description="Help us improve by sharing your feedback",
        inputs=[
            RadioButton(
                name="satisfaction",
                title="How satisfied are you?",
                options=["very_satisfied", "satisfied", "neutral", "dissatisfied"],
                option_labels=["Very Satisfied", "Satisfied", "Neutral", "Dissatisfied"],
                required=True
            ),
            TextArea(
                name="feedback",
                title="Additional Comments",
                placeholder="Share your thoughts...",
                required=False
            )
        ],
        submit_text="Submit Survey",
        on_event=[
            MessageEvent(
                message="Thank you for completing the survey! Your feedback helps us improve.",
                map_input_to="submit"
            )
        ]
    )
    
    return ToolResult(
        content=[
            TextContent(
                text="Please take a moment to complete our survey",
                annotations=Annotations(
                    audience=[Role.USER]
                )
            )
        ],
        widget=form
    )

Using type: "message" with dictionary format

When using the dictionary return format, configure on_event within the _meta field: The following example shows the basic structure of a message event configuration in dictionary format:
"on_event": [
    {
        "type": "message",
        "message": "Registration completed successfully",
        "map_input_to": "submit"
    }
]


The following example demonstrates a feedback form that sends a message to the agent upon submission:
@tool()
def show_feedback_form():
    """Display feedback form that sends a message to the agent."""
    return {
        "content": [
            {
                "type": "text",
                "annotations": {
                    "audience": ["user"]
                },
                "text": "Please provide your feedback"
            }
        ],
        "_meta": {
            "com.ibm.orchestrate/widget": {
                "response_type": "forms",
                "json_schema": {
                    "title": "Feedback Form",
                    "type": "object",
                    "properties": {
                        "rating": {"type": "string", "title": "Rating"},
                        "comments": {"type": "string", "title": "Comments"}
                    }
                },
                "ui_schema": {
                    "rating": {"ui:widget": "RadioButtonWidget"},
                    "comments": {"ui:widget": "TextAreaWidget"}
                },
                "form_data": {"rating": "", "comments": ""},
                "on_event": [
                    {
                        "type": "message",
                        "message": "Thank you for your feedback! Your response has been recorded.",
                        "map_input_to": "submit"
                    }
                ]
            }
        },
        "structuredContent": None,
        "context_updates": {}
    }

Custom widgets with user_defined

Create flexible UI components with custom widgets by rendering your own rich element logic. These widgets are passed through the _meta parameter and are rendered in embedded chat by using the userDefinedResponse event. Custom widgets in tool responses is a custom solution that operates independently of standardized UI protocols such as MCP-UI or A2UI. Application builders are responsible for implementing the rendering logic and managing security considerations.
The user_defined response type from tools is supported only in embedded chat. The webpage builder is responsible for implementing the rendering logic.
The following example demonstrates how to create a custom widget with user-defined response type and structured data:
from ibm_watsonx_orchestrate.agent_builder.tools import tool
from datetime import datetime, timezone

@tool
def get_custom_widget(
    widget_type: str = "custom_component",
    title: str = "Custom Widget",
    message: str = "This is a custom user-defined widget",
    action_url: str = "https://ibm.com"
):
    """
    Generate a custom widget with user_defined response type.
    
    This tool creates a custom widget that can be rendered in the watsonx Orchestrate
    embedded chat interface using custom rendering logic.
    """
    
    return {
        "content": [
            {
                "type": "text",
                "annotations": {
                    "audience": ["user"]
                },
                "text": f"{title}: {message}"
            }
        ],
        "_meta": {
            "com.ibm.orchestrate/widget": {
                "response_type": "user_defined",
                "user_defined": {
                    "user_defined_type": widget_type,
                    "title": title,
                    "text": message,
                    "timestamp": datetime.now(timezone.utc).isoformat(),
                    "action_url": action_url,
                    "custom_field": "You can add any custom data here",
                    "interactive": True
                }
            }
        },
        "structuredContent": None,
        "context_updates": {}
    }
Custom widget structure
When creating custom widgets, you must structure the metadata in a specific nested format that the chat can recognize and process. Place the widget data within the _meta parameter of your tool response by using the standardized namespace key to ensure proper routing to the userDefinedResponse event handler. The following example shows the required structure for custom widget metadata with detailed annotations:
_meta={
    # Namespace key that identifies this as an widget
    "com.ibm.orchestrate/widget": {
        # Indicates this is a custom user-defined response type
        "response_type": "user_defined",
        
        # Container object that holds all your custom widget properties
        # All custom data must be nested within this object
        "user_defined": {
            # Required: Identifies the specific type of widget being rendered
            # Use this to differentiate between different widget implementations in your handler
            "user_defined_type": "your_widget_type",
            
            # Optional: Display title for the widget
            "title": "Widget Title",
            
            # Optional: Main content or message text
            "text": "Widget content",
            
            # Add any additional custom properties your widget needs
            # Examples: timestamps, URLs, status flags, nested objects, arrays, etc.
            # All properties here are accessible in the userDefinedResponse event handler
        }
    }
}
Where:
  • Container structure: Nest all custom properties inside the user_defined object. This object acts as a container that isolates your custom data from the standard response structure.
  • Widget identification: Use the required user_defined_type field to identify your widget type. Your rendering logic uses this field to determine how to display the widget.
  • Flexible properties: Beyond the required user_defined_type, include any custom properties your widget needs, such as title, text, data, config, or domain-specific fields. The structure adapts to your requirements.
  • Multiple widget types: This flexible structure accommodates various widget types, from simple cards and notifications to complex dashboards and interactive components. Implement and test the rendering logic for each widget type to ensure proper display.
Rendering custom widgets
The webpage builder must implement the rendering logic for custom widgets in embedded chat. For complete implementation details, examples, and event handling, see the userDefinedResponse event documentation.