Command Center Forms
When an AppComponent widget needs a specialized input experience, the right object to return is EditableFormDefinition.
This is the form contract used when the default argument resolution is not enough.
Use it when you need things like:
- grouped sections
- domain-specific labels
- richer field kinds such as percent
- explicit defaults
- custom field tokens for downstream binding or draft state
When You Do Not Need A Custom Form
For simple function arguments, Command Center can resolve the default form directly from the operation contract.
That means if your endpoint or function only exposes normal flat arguments with a straightforward shape, you usually do not need to add a custom form.
Typical examples:
- a few scalar query parameters
- a small request body with standard primitive fields
- simple enums, booleans, dates, and numbers
In those cases, let Command Center generate the form automatically.
When You Should Return EditableFormDefinition
Use EditableFormDefinition when you want to control how the form is presented, especially for AppComponent widgets that should render a specialized form with grouped sections and explicit field definitions.
This is the right path when:
- the autogenerated form is too flat
- you want a product-specific language instead of raw parameter names
- you need sectioned inputs
- you want more control over editable vs read-only fields
- you want percent or formatter hints
- you need stable field tokens for app-specific behavior
The Core Objects
The SDK models are:
EditableFormDefinitionFormSectionDefinitionFormFieldDefinitionFormFieldKind
These live in:
mainsequence.client.command_center
The important mental model is:
- one form
- one or more sections
- each section contains fields
- each field has a stable token, a user-facing label, a kind, and editability rules
Example
from fastapi import APIRouter
from mainsequence.client.command_center import (
EditableFormDefinition,
FormFieldDefinition,
FormFieldKind,
FormSectionDefinition,
)
router = APIRouter()
@router.get(
"/pricing-context/{product_id}",
response_model=EditableFormDefinition,
)
def get_pricing_context_form(product_id: str) -> EditableFormDefinition:
return EditableFormDefinition(
form_id=f"pricing-context:{product_id}:v1",
title="NOTE",
description="custom note based on DoubleNoTouchMXN with additional funding parameter.",
sections=[
FormSectionDefinition(
id="custom_fields",
title="Custom Fields",
fields=[
FormFieldDefinition(
token="custom::funding_rate",
name="funding_rate",
label="Funding Rate",
kind=FormFieldKind.PERCENT,
editable=True,
required=True,
value=None,
default_value=None,
formatter="percent",
),
FormFieldDefinition(
token="custom::notional",
name="notional",
label="Notional",
kind=FormFieldKind.NUMBER,
editable=True,
required=False,
value=100,
default_value=100,
),
],
)
],
)
What This Example Is Doing
form_idgives the form a stable identity for reset/reload semanticstitleanddescriptioncontrol the form headersectionslet you group fields into meaningful blockstokenis the stable field identity used for draft state and downstream bindingsnameis the backend field identifierlabelis what the user seeskindtells Command Center how to render the fieldformattergives the frontend an extra presentation hint
Why token Matters
The token field is not cosmetic.
It is the stable global identifier for the field in the Command Center interaction model. That is what makes it useful for:
- draft state
- bindings
- app-specific coordination
If you are defining a custom form, keep tokens stable across compatible versions.
Choosing The Right FormFieldKind
The built-in field kinds are:
STRINGNUMBERINTEGERBOOLEANDATEDATETIMEPERCENTENUMJSON
Use the kind that reflects the business meaning, not just the raw transport type.
Example:
PERCENTis better thanNUMBERfor a funding rate if the UI should treat it as a percentage input
A Practical Rule
Start with the default generated form.
Only introduce EditableFormDefinition when you actually need:
- sectioned layout
- specialized labels
- richer formatting
- explicit token control
That keeps the Command Center integration simpler and makes the custom form a deliberate choice instead of a default habit.
Relationship To AppComponent Widgets
This page is specifically relevant for AppComponent widgets.
The usual pattern is:
- the widget resolves an operation contract
- Command Center can often build the form automatically from that contract
- if that is not enough, the app returns
EditableFormDefinition
So the custom form is an override for richer UX, not a requirement for every AppComponent.
It is also important not to mix this with widget output contracts.
EditableFormDefinitiondescribes how the widget should collect input- the models in
mainsequence.client.command_center.data_modelsdescribe the exact output shape expected by some widgets when your API feeds them directly
If your API is powering a widget end-to-end, you may use both:
- a form contract for the widget input
- a widget data contract for the widget output