# Inputs Configuration

Complete reference for defining action inputs in Falcon connectors. Covers all input types, field properties, locations, and mapping to request parameters.

## Input Schema

```yaml
inputs:
  - name: fieldName              # Parameter name (required)
    description: Field description    # Full description (required)
    type: string                 # Data type (required, unless variants is used)
    required: true               # Whether required (required)
    in: body                     # Location (required, top-level only)
    array: false                 # Whether this is an array (optional)
    properties: []               # Nested fields for object type (optional)
    oneOf:                       # Enum values for enum type (optional)
      values:
        - value1
        - value2
    rules: {}                    # Validation rules (optional)
    variants: []                 # Multiple accepted shapes (optional, replaces type)
```

## Required Fields

| Field | Type | Description |
| :--- | :--- | :--- |
| `name` | string | Parameter name — must match the provider's parameter name. If the provider uses special characters (e.g. `$filter`, `first-name`), simplify to a clean name (e.g. `filter`, `first_name`). The mapping to the actual provider param name happens in args. |
| `description` | string | Full description of the parameter |
| `type` | string | Data type: `string`, `number`, `boolean`, `enum`, `object`, `datetime_string` — required unless `variants` is used |
| `required` | boolean | Whether parameter is required |
| `in` | string | Location: `path`, `query`, `body`, `headers` — top-level inputs only; nested properties inside `object` do not use `in` |

## Optional Fields

| Field | Type | Description |
| :--- | :--- | :--- |
| `array` | boolean | `true` when the input is an array — use element type in `type`, never `type: array` |
| `properties` | array | Nested field definitions — for `type: object` only; nested properties omit `in` |
| `oneOf` | object | Allowed enum values — for `type: enum` only |
| `rules` | object | Validation rules for the input |
| `variants` | array | Multiple accepted shapes — when present, `type` must be omitted. Each variant has its own `type`, `description`, `array`, `properties`, `oneOf`, `rules`. Min 2 variants required. |

## Input Types

### String

```yaml
- name: email
  description: User's email address
  type: string
  required: true
  in: body
```

### Number

```yaml
- name: limit
  description: Maximum number of results to return
  type: number
  required: false
  in: query
```

### Boolean

```yaml
- name: includeInactive
  description: Include inactive users in results
  type: boolean
  required: false
  in: query
```

### Datetime String

```yaml
- name: createdAfter
  description: Filter by creation date (ISO 8601 format)
  type: datetime_string
  required: false
  in: query
```

### Enum

```yaml
- name: status
  description: Filter by user status
  type: enum
  required: false
  in: query
  oneOf:
    values:
      - active
      - inactive
      - suspended
```

**Enum values are always strings — even for numeric wire codes.** Write `'1'`, `'2'`,
`'3'` for a coded priority/status/severity field, never bare `1`, `2`, `3`. The schema
requires it (the corpus is unanimous: 0 of 94,020 shipped enum values are bare numbers,
2,358 are quoted numeric strings), and `stackone validate` rejects bare numbers with a
**misleading** "Unknown field … should be removed" error, not a type error — a measured
run burned a three-stage repair loop decoding that message. Record them as strings in the
research JSON too, so B5's research-vs-connector enum comparison matches. `autofix.js`
quotes bare numerics under `values:` as a safety net.

```yaml
- name: priority
  type: enum
  oneOf:
    values:
      - '1'    # low — quoted string, not 1
      - '2'    # medium
      - '3'    # high
```

### Array of Enums

```yaml
- name: statuses
  description: Filter by one or more user statuses
  type: enum
  array: true
  required: false
  in: query
  oneOf:
    values:
      - active
      - inactive
      - suspended
```

### Object

```yaml
- name: address
  description: User's address information
  type: object
  required: false
  in: body
  properties:
    - name: street
      description: Street address
      type: string
      required: true

    - name: city
      description: City name
      type: string
      required: true

    - name: zipCode
      description: Postal code
      type: string
      required: false
```

### Array (Simple)

**⚠️ NEVER use `type: array`**. Use `array: true` with element type:

```yaml
- name: userIds
  description: Array of user IDs to process
  type: string        # Element type
  array: true         # Makes it an array
  required: true
  in: body
```

### Array of Objects

```yaml
- name: fields
  description: Array of field objects
  type: object        # Element type is object
  array: true         # Makes it array of objects
  required: false
  in: body
  properties:
    - name: key
      description: Field key
      type: string
      required: true

    - name: value
      description: Field value
      type: string
      required: true

    - name: type
      description: Field type
      type: enum
      required: false
      oneOf:
        values:
          - STRING
          - NUMBER
          - BOOLEAN
```

### Variant Inputs

When a single input field can accept multiple distinct shapes (e.g. a string OR a boolean OR an array of objects), use `variants` instead of a single `type`. Each variant is a complete shape spec with its own `type`, and optionally `description`, `array`, `properties`, `oneOf`, and `rules`.

**Rules:**
- When `variants` is present, `type`, `array`, `properties`, `oneOf`, and `rules` must NOT be set on the parent input
- Minimum 2 variants required
- Each variant must have a `type`
- Variants cannot be nested inside other variants
- Enum variants require `oneOf`
- Callers send raw values — `z.union` with strict objects picks the right variant at runtime (first match wins)
- In MCP tool schemas, variants are exposed as `anyOf` with per-variant descriptions

```yaml
- name: value
  description: The value to set for this operation
  required: true
  in: body
  variants:
    - type: string
      description: A plain string value (e.g. name, status, locale)
    - type: boolean
      description: A boolean flag value
    - type: object
      array: true
      description: An array of entity references by ID
      properties:
        - name: id
          description: The unique identifier
          type: string
          required: true
    - type: object
      array: true
      description: An array of entity references by name
      properties:
        - name: name
          description: The name of the entity
          type: string
          required: true
    - type: enum
      description: A predefined status value
      oneOf:
        values:
          - active
          - inactive
```

**When to use variants:**
- The provider API accepts different shapes for the same field depending on context (e.g. assign by ID vs by name)
- A field can be a primitive OR a complex object
- A PATCH/UPDATE operation where the value type depends on which property is being updated

**When NOT to use variants:**
- The field has a single, predictable type — just use `type` directly
- The field is optional — use `required: false`, not variants
- The field accepts any value — use `type: object` without properties

**Args mapping** — variant inputs are mapped exactly like regular inputs. Pass the top-level input; the runtime handles variant selection:

```yaml
args:
  - name: value
    value: $.inputs.value
    in: body
```

## Input Locations

### in: path

Parameters in URL path (use with string interpolation):

```yaml
- name: userId
  description: User ID
  type: string
  required: true
  in: path

# Maps to URL:
url: /users/${inputs.userId}
```

### in: query

Query string parameters:

```yaml
- name: limit
  description: Page size
  type: number
  required: false
  in: query

# Maps to:
args:
  - name: limit
    value: $.inputs.limit
    in: query
    condition: '{{present(inputs.limit)}}'
```

### in: body

Request body parameters:

```yaml
- name: email
  description: User email
  type: string
  required: true
  in: body

# Maps to:
args:
  - name: email
    value: $.inputs.email
    in: body
```

### in: headers

Custom headers (rare):

```yaml
- name: customHeader
  description: Custom header value
  type: string
  required: false
  in: headers

# Maps to:
args:
  - name: X-Custom-Header
    value: $.inputs.customHeader
    in: headers
```

## Nested Objects

```yaml
- name: user
  description: Complete user object
  type: object
  required: true
  in: body
  properties:
    - name: profile
      description: Profile information
      type: object
      required: true
      properties:
        - name: firstName
          description: First name
          type: string
          required: true
        - name: age
          description: Age of the user
          type: number
          required: false
        - name: role
          description: User role
          type: enum
          required: false
          oneOf:
            values:
              - admin
              - member
              - guest
        - name: tags
          description: List of tags assigned to the user
          type: string
          array: true
          required: false
    - name: address
      description: Address information
      type: object
      required: false
      properties:
        - name: street
          description: Street address
          type: string
          required: false

        - name: city
          description: City name
          type: string
          required: false
```

## Mapping Inputs to Args

### Simple Mapping

```yaml
# Input definition
inputs:
  - name: email
    type: string
    required: true
    in: body

# Step function args
args:
  - name: email
    value: $.inputs.email
    in: body
```

### Conditional Mapping (Optional Inputs)

```yaml
# Input definition
inputs:
  - name: limit
    type: number
    required: false
    in: query

# Step function args
args:
  - name: limit
    value: $.inputs.limit
    in: query
    condition: '{{present(inputs.limit)}}'  # Only include if provided
```

### Object Mapping

```yaml
# Input definition
inputs:
  - name: address
    type: object
    required: true
    in: body
    properties:
      - name: street
        type: string
        required: true
      - name: city
        type: string
        required: true

# Step function args — pass top-level object only; nested properties are included automatically
args:
  - name: address
    value: $.inputs.address
    in: body
```

### Array Mapping

```yaml
# Input definition
inputs:
  - name: tags
    type: string
    array: true
    required: false
    in: body

# Step function args
args:
  - name: tags
    value: $.inputs.tags
    in: body
    condition: '{{present(inputs.tags)}}'
```

### Array of Objects Mapping

```yaml
# Input definition
inputs:
  - name: fields
    description: Array of field objects
    type: object
    array: true
    required: false
    in: body
    properties:
      - name: key
        description: Field key
        type: string
        required: true
      - name: value
        description: Field value
        type: string
        required: true

# Step function args
args:
  - name: fields
    value: $.inputs.fields  # ✅ Pass entire array directly
    in: body
    condition: '{{present(inputs.fields)}}'

# Provider will receive:
# {
#   "fields": [
#     {"key": "name", "value": "John"},
#     {"key": "email", "value": "john@example.com"}
#   ]
# }
```

**Note:** For array of objects, pass the entire array - don't try to map individual properties. The array structure is preserved.

## Related Skills

- **action-config**: Action configuration (where inputs are defined)
- **step-function-request**: HTTP request configuration

## Critical Rules

### MUST DO:
- ✅ **Map EVERY parameter from provider API documentation**
- ✅ **Include ALL optional and required parameters**
- ✅ Try to match provider's actual API parameters exactly
- ✅ Use `array: true` (never `type: array`)
- ✅ Define `properties` for `type: object` when the nested structure is predefined — skip `properties` only for dynamic key-value pairs
- ✅ Define `oneOf.values` for enum types
- ✅ Use correct `in` location (path/query/body)
- ✅ Add `condition` for optional parameters in args
- ✅ Use JSONPath for value references: `$.inputs.fieldName`

### MUST NOT:
- ❌ Create inputs that don't exist in research docs
- ❌ Use `type: array` (use `array: true` with element type)
- ❌ Skip `description` field
- ❌ Use wrong `in` location
- ❌ Forget `condition` for optional args

---

**Remember**: Complete coverage is critical. Map EVERY parameter from provider API - required AND optional. Use provider's documentation as source of truth.
