# Module Schema Reference

#### Schema Structure <a href="#schema-structure" id="schema-structure"></a>

**Root Schema Object**

```
{
  "metadata": {
    "workflows_module_schema_version": "1.0.0"
  },
  "fields": [...],
  "ui_options": {...}
}
```

**Required Properties:**

* `metadata` - Schema metadata configuration
* `fields` - Array of field definitions

**Optional Properties:**

* `ui_options` - Global UI configuration options

***

#### Metadata Configuration <a href="#metadata-configuration" id="metadata-configuration"></a>

**workflows\_module\_schema\_version**

* **Type:** String
* **Required:** Yes
* **Default:** "1.0.0"
* **Description:** Specifies the schema version for compatibility

```
{
  "metadata": {
    "workflows_module_schema_version": "1.0.0"
  }
}
```

***

#### Field Types <a href="#field-types" id="field-types"></a>

**String Fields**

**Basic String Field:**

```
{
  "id": "field_name",
  "type": "string",
  "label": "Display Label",
  "description": "Field description",
  "default": "Default value"
}
```

**Properties:**

* `id` (string, required) - Unique field identifier
* `type` (string, required) - Must be "string"
* `label` (string, optional) - Display label for the field
* `description` (string, optional) - Help text for users
* `default` (string, optional) - Default value

**Number Fields**

**Basic Number Field:**

```
{
  "id": "count",
  "type": "number",
  "label": "Count",
  "default": 0
}
```

**Properties:**

* `id` (string, required) - Unique field identifier
* `type` (string, required) - Must be "number"
* `label` (string, optional) - Display label
* `default` (number, optional) - Default numeric value

**Integer Fields**

**Basic Integer Field:**

```
{
  "id": "whole_number",
  "type": "integer",
  "label": "Whole Number",
  "default": 1
}
```

**Boolean Fields**

**Basic Boolean Field:**

```
{
  "id": "enable_feature",
  "type": "boolean",
  "label": "Enable Feature",
  "default": false
}
```

**Connection Fields**

**Connection Field:**

```
{
  "type": "connection",
  "id": "api_connection",
  "label": "API Connection",
  "allowed_app_types": ["salesforce", "hubspot"],
  "allowed_connection_management_types": ["managed", "custom"]
}
```

**Properties:**

* `allowed_app_types` (array, required) - List of supported connection types
* `allowed_connection_management_types` (array, required) - Connection management options

**Supported App Types:**

* `salesforce` - Salesforce CRM
* `hubspot` - HubSpot CRM
* `postgres` - PostgreSQL database
* `mysql` - MySQL database
* `custom_api` - Custom API connections

**Connection Management Types:**

* `managed` - Platform-managed connections
* `custom` - User-provided credentials

**Object Fields**

**Simple Object Field:**

```
{
  "type": "object",
  "id": "settings",
  "label": "Settings",
  "fields": [
    {
      "id": "name",
      "type": "string",
      "label": "Name"
    }
  ]
}
```

**Object with Choices:**

```
{
  "type": "object",
  "id": "platform",
  "label": "Platform",
  "choices": {
    "values": [
      {
        "value": { "id": "linkedin", "label": "LinkedIn" },
        "label": "LinkedIn"
      }
    ]
  }
}
```

**Array Fields**

**String Array:**

```
{
  "type": "array",
  "id": "tags",
  "label": "Tags",
  "items": {
    "type": "string",
    "label": "Tag"
  }
}
```

**Object Array:**

```
{
  "type": "array",
  "id": "users",
  "label": "Users",
  "items": {
    "type": "object",
    "fields": [
      {
        "id": "name",
        "type": "string",
        "label": "Name"
      }
    ]
  }
}
```

***

#### UI Widgets <a href="#ui-widgets" id="ui-widgets"></a>

**Available Widgets**

**input** (default for strings)

```
{
  "ui_options": {
    "ui_widget": "input"
  }
}
```

**textarea** (multi-line text)

```
{
  "ui_options": {
    "ui_widget": "textarea"
  }
}
```

**password** (hidden text input)

```
{
  "ui_options": {
    "ui_widget": "password"
  }
}
```

**SelectWidget** (dropdown selection)

```
{
  "ui_options": {
    "ui_widget": "SelectWidget"
  }
}
```

**checkbox** (boolean checkbox)

```
{
  "ui_options": {
    "ui_widget": "checkbox"
  }
}
```

**CodeblockWidget** (code editor)

```
{
  "ui_options": {
    "ui_widget": "CodeblockWidget",
    "ui_options": {
      "language": "json"
    }
  }
}
```

**hidden** (hidden field)

```
{
  "ui_options": {
    "ui_widget": "hidden"
  }
}
```

**CodeblockWidget Languages**

Supported syntax highlighting languages:

* `json` - JSON format
* `sql` - SQL queries
* `javascript` - JavaScript code
* `python` - Python code
* `yaml` - YAML configuration
* `xml` - XML markup
* `html` - HTML markup
* `css` - CSS styling

***

#### Validation Rules <a href="#validation-rules" id="validation-rules"></a>

**Basic Validation**

**required** - Field is mandatory

```
{
  "validation": {
    "required": true
  }
}
```

**min\_length** - Minimum string length

```
{
  "validation": {
    "min_length": 3
  }
}
```

**max\_length** - Maximum string length

```
{
  "validation": {
    "max_length": 100
  }
}
```

**minimum** - Minimum numeric value

```
{
  "validation": {
    "minimum": 0
  }
}
```

**maximum** - Maximum numeric value

```
{
  "validation": {
    "maximum": 1000
  }
}
```

**pattern** - Regular expression validation

```
{
  "validation": {
    "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
  }
}
```

**Format Validation**

**email** - Email address format

```
{
  "format": "email"
}
```

**uri** - URI format

```
{
  "format": "uri"
}
```

**date** - Date format (YYYY-MM-DD)

```
{
  "format": "date"
}
```

**date-time** - Date-time format (ISO 8601)

```
{
  "format": "date-time"
}
```

**uuid** - UUID format

```
{
  "format": "uuid"
}
```

***

#### Dynamic Content <a href="#dynamic-content" id="dynamic-content"></a>

**Basic Content Configuration**

```
{
  "content": {
    "type": ["managed"],
    "content_objects": [
      {
        "id": "users"
      }
    ]
  }
}
```

**Properties:**

* `type` (array, required) - Content management types
* `content_objects` (array, required) - List of content objects to fetch

**Content Management Types:**

* `managed` - Platform manages the content loading
* `custom` - Custom content loading logic

**Content Dependencies**

**Field-dependent content:**

```
{
  "content": {
    "type": ["managed"],
    "content_objects": [
      {
        "id": "users_in_channel",
        "content_object_depends_on_fields": [
          {
            "id": "channel"
          }
        ]
      }
    ]
  }
}
```

**Array item dependencies:**

```
{
  "content_object_depends_on_fields": [
    {
      "id": "channels.items"
    }
  ]
}
```

***

#### Choices Configuration <a href="#choices-configuration" id="choices-configuration"></a>

**Static Choices**

**Simple string choices:**

```
{
  "choices": {
    "values": [
      { "value": "option1", "label": "Option 1" },
      { "value": "option2", "label": "Option 2" }
    ]
  }
}
```

**Object choices:**

```
{
  "choices": {
    "values": [
      {
        "value": { "id": "contact", "label": "Contact" },
        "label": "Contact"
      },
      {
        "value": { "id": "lead", "label": "Lead" },
        "label": "Lead"
      }
    ]
  }
}
```

**Dynamic Choices**

**Empty choices for dynamic loading:**

```
{
  "choices": {
    "values": []
  }
}
```

***

#### Schema Rules (Conditional Logic) <a href="#schema-rules-conditional-logic" id="schema-rules-conditional-logic"></a>

**Basic Rules Structure**

```
{
  "rules": [
    {
      "if": {
        "and": [
          {
            "id": "field_id",
            "operator": "equal",
            "value": "expected_value"
          }
        ]
      },
      "then": {
        "fields": [
          {
            "id": "target_field",
            "ui_options": {
              "ui_widget": "hidden"
            },
            "apply_as": "merge"
          }
        ]
      }
    }
  ]
}
```

**Condition Operators**

**equal** - Exact match

```
{
  "id": "status",
  "operator": "equal",
  "value": "active"
}
```

**not\_equal** - Not equal to

```
{
  "id": "status",
  "operator": "not_equal",
  "value": "inactive"
}
```

**is\_in** - Value in array

```
{
  "id": "category",
  "operator": "is_in",
  "value": ["premium", "enterprise"]
}
```

**is\_not\_in** - Value not in array

```
{
  "id": "category",
  "operator": "is_not_in",
  "value": ["free", "trial"]
}
```

**is\_empty** - Field is empty

```
{
  "id": "optional_field",
  "operator": "is_empty"
}
```

**is\_not\_empty** - Field has value

```
{
  "id": "required_field",
  "operator": "is_not_empty"
}
```

**greater\_than** - Numeric comparison

```
{
  "id": "count",
  "operator": "greater_than",
  "value": 10
}
```

**less\_than** - Numeric comparison

```
{
  "id": "count",
  "operator": "less_than",
  "value": 100
}
```

**Logic Combinators**

**and** - All conditions must be true

```
{
  "and": [
    { "id": "field1", "operator": "equal", "value": "value1" },
    { "id": "field2", "operator": "equal", "value": "value2" }
  ]
}
```

**or** - Any condition must be true

```
{
  "or": [
    { "id": "field1", "operator": "equal", "value": "value1" },
    { "id": "field2", "operator": "equal", "value": "value2" }
  ]
}
```

**Rule Effects**

**apply\_as** options:

* `merge` - Merge with existing field configuration
* `fully_replace` - Replace entire field configuration

**Show/Hide Fields:**

```
{
  "then": {
    "fields": [
      {
        "id": "conditional_field",
        "ui_options": {
          "ui_widget": null
        },
        "apply_as": "merge"
      }
    ]
  }
}
```

**Hide Fields:**

```
{
  "then": {
    "fields": [
      {
        "id": "conditional_field",
        "ui_options": {
          "ui_widget": "hidden"
        },
        "apply_as": "merge"
      }
    ]
  }
}
```

***

#### UI Layout Options <a href="#ui-layout-options" id="ui-layout-options"></a>

**Global UI Options**

**Field Ordering:**

```
{
  "ui_options": {
    "ui_order": ["field1", "field2", "field3"]
  }
}
```

**Field-Level UI Options**

**Horizontal Layout:**

```
{
  "ui_options": {
    "ui_layout": {
      "type": "horizontal",
      "elements": ["first_name", "last_name"]
    }
  }
}
```

***

#### Advanced Features <a href="#advanced-features" id="advanced-features"></a>

**Action Handlers**

Action handlers define what should happen when users interact with fields. They trigger specific behaviors when field values change.

**load\_schema**

**Purpose:** Triggers a schema reload when the field value changes, allowing for dynamic updates to other fields based on the new value.

**Basic Usage:**

```
{
  "id": "country",
  "type": "string",
  "label": "Country",
  "on_action": {
    "load_schema": true
  }
}
```

**What happens when `load_schema` is triggered:**

1. You change the value of the field `country`
2. A trigger sends an HTTP POST request to the module /schema endpoint with :

* the full schema
* the full form data
* the full content objects

1. The module returns a new schema
2. The new schema is merged with the existing schema
3. The frontend updates the UI to reflect the new schema

**Common Use Cases**

**Use Case 1: Cascading Dropdowns**

```
{
  "id": "country",
  "type": "object",
  "label": "Country",
  "on_action": {
    "load_schema": true
  },
  "choices": {
    "values": [
      {"value": {"id": "us", "label": "United States"}, "label": "United States"},
      {"value": {"id": "ca", "label": "Canada"}, "label": "Canada"}
    ]
  }
},
{
  "id": "state",
  "type": "object",
  "label": "State/Province",
  "content": {
    "type": ["managed"],
    "content_objects": [
      {
        "id": "states_by_country",
        "content_object_depends_on_fields": [
          {"id": "country"}
        ]
      }
    ]
  }
}
```

**Use Case 2: Conditional Field Display**

```
{
  "id": "data_source",
  "type": "string",
  "label": "Data Source",
  "on_action": {
    "load_schema": true
  },
  "choices": {
    "values": [
      {"value": "database", "label": "Database"},
      {"value": "api", "label": "API"},
      {"value": "file", "label": "File"}
    ]
  }
},
{
  "id": "database_config",
  "type": "object",
  "label": "Database Configuration",
  "rules": [
    {
      "if": {
        "and": [
          {
            "id": "data_source",
            "operator": "equal",
            "value": "database"
          }
        ]
      },
      "then": {
        "fields": [
          {
            "id": "database_config",
            "ui_options": {
              "ui_widget": null
            },
            "apply_as": "merge"
          }
        ]
      }
    }
  ]
}
```

**Use Case 3: Dynamic Validation**

```
{
  "id": "field_type",
  "type": "string",
  "label": "Field Type",
  "on_action": {
    "load_schema": true
  },
  "choices": {
    "values": [
      {"value": "email", "label": "Email"},
      {"value": "phone", "label": "Phone"},
      {"value": "text", "label": "Text"}
    ]
  }
},
{
  "id": "field_value",
  "type": "string",
  "label": "Field Value",
  "rules": [
    {
      "if": {
        "and": [
          {
            "id": "field_type",
            "operator": "equal",
            "value": "email"
          }
        ]
      },
      "then": {
        "fields": [
          {
            "id": "field_value",
            "format": "email",
            "validation": {
              "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
            },
            "apply_as": "merge"
          }
        ]
      }
    }
  ]
}
```

**Backend Implementation**

When `load_schema: true` is triggered, your `/content` endpoint receives the updated form data:

```
@router.route("/content", methods=["POST"])
def content():
    try:
        request = Request(flask_request)
        data = request.data

        # Get updated form data after field change
        form_data = data.get("form_data", {})
        content_object_names = data.get("content_object_names", [])

        content_objects = []

        for content_name in content_object_names:
            if content_name == "states_by_country":
                # Get the selected country from updated form data
                selected_country = form_data.get("country", {})
                country_id = selected_country.get("id") if selected_country else None

                if country_id == "us":
                    states = [
                        {"value": {"id": "ca", "label": "California"}, "label": "California"},
                        {"value": {"id": "ny", "label": "New York"}, "label": "New York"}
                    ]
                elif country_id == "ca":
                    states = [
                        {"value": {"id": "on", "label": "Ontario"}, "label": "Ontario"},
                        {"value": {"id": "bc", "label": "British Columbia"}, "label": "British Columbia"}
                    ]
                else:
                    states = []

                content_objects.append({
                    "content_object_name": "states_by_country",
                    "data": states
                })

        return Response(data={"content_objects": content_objects})

    except Exception as e:
        return Response.error(str(e))
```

**Performance Considerations**

* **Use Sparingly:** Only add `load_schema: true` to fields that actually need to trigger updates
* **Optimize Backend:** Ensure your `/content` endpoint responds quickly to avoid UI lag
* **Cache When Possible:** Cache static data that doesn't change between requests
* **Batch Updates:** If multiple fields need to trigger updates, consider grouping them

**Best Practices**

1. **Clear User Feedback:** Show loading indicators when schema is reloading
2. **Preserve User Input:** Ensure field values aren't lost during reload
3. **Error Handling:** Gracefully handle failures in content loading
4. **Debouncing:** Consider debouncing rapid field changes to avoid excessive requests

**Debugging Tips**

* Check browser network tab to see `/content` requests when fields change
* Verify that `content_object_depends_on_fields` references match your field IDs exactly
* Test with browser console open to catch any JavaScript errors
* Use simple test data first, then add complexity

***

#### Complete Field Example <a href="#complete-field-example" id="complete-field-example"></a>

Here's a comprehensive example showing most available options:

```
{
  "id": "user_email",
  "type": "string",
  "label": "User Email",
  "description": "Enter the user's email address",
  "default": "",
  "format": "email",
  "validation": {
    "required": true,
    "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
  },
  "validation_messages": {
    "required": "Email is required for notifications",
    "pattern": "Please enter a valid email address"
  },
  "ui_options": {
    "ui_widget": "input"
  },
  "on_action": {
    "load_schema": true
  },
  "rules": [
    {
      "if": {
        "and": [
          {
            "id": "user_email",
            "operator": "is_not_empty"
          }
        ]
      },
      "then": {
        "fields": [
          {
            "id": "send_notification",
            "ui_options": {
              "ui_widget": null
            },
            "apply_as": "merge"
          }
        ]
      }
    }
  ]
}
```

This specification covers all major aspects of module schema configuration. Refer to this document when building your schemas to understand all available options and their proper usage.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.stacksync.com/agentic-workflows/developers/module-schema-reference.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
