> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Step Functions

> Reference for connector action steps and the step functions that execute API requests, transform data, and control flow.

## `steps`

**Impact:** ⚙️ Runtime

Define the execution flow for the action. Steps run sequentially.

```yaml theme={null}
steps:
  - stepId: fetch_employees
    description: Fetch employee data from API
    stepFunction:
      functionName: request
      parameters:
        url: '/employees/directory'
        method: get
        args:
          - name: fields
            value: '{{$.inputs.fields ?? "firstName,lastName"}}'
            in: query
  - stepId: transform_response
    stepFunction:
      functionName: map_fields
      version: '2'
      parameters:
        fields:
          - targetFieldKey: employees
            expression: $.employees
        dataSource: $.steps.fetch_employees.output.data
```

### Step Properties

| Property        | Type     | Description                                                                         |
| --------------- | -------- | ----------------------------------------------------------------------------------- |
| `stepId`        | string   | Unique identifier within action                                                     |
| `description`   | string   | What this step does (for debugging)                                                 |
| `condition`     | JEXL     | Skip step if condition is false                                                     |
| `ignoreError`   | boolean  | Continue execution if this step fails (default: `false`)                            |
| `stepFunction`  | object   | Function to execute (for simple steps)                                              |
| `iterator`      | JSONPath | Array expression — makes this a foreach step. See [Iterator Steps](#iterator-steps) |
| `stepFunctions` | array    | Multiple functions to execute per iteration (use with `iterator`)                   |

### Conditional Steps

```yaml theme={null}
steps:
  - stepId: fetch_with_filter
    condition: '{{present(inputs.department)}}'
    stepFunction:
      functionName: request
      parameters:
        url: '/employees'
        args:
          - name: department
            value: $.inputs.department
            in: query
```

<Accordion title="Condition expressions">
  Conditions use JEXL syntax with helper functions:

  | Function     | Description                        | Example                  |
  | ------------ | ---------------------------------- | ------------------------ |
  | `present(x)` | Value exists and not null          | `present(inputs.filter)` |
  | `blank(x)`   | Value is null, undefined, or empty | `blank(inputs.cursor)`   |

  **Common patterns:**

  ```yaml theme={null}
  # Only run if parameter provided
  condition: '{{present(inputs.employee_id)}}'

  # Only run on first page
  condition: '{{blank(inputs.cursor)}}'

  # Multiple conditions
  condition: '{{present(inputs.start_date) && present(inputs.end_date)}}'
  ```
</Accordion>

***

## Step Functions

### `request`

**Impact:** ⚙️ Runtime

Make an HTTP request to the provider API.

```yaml theme={null}
stepFunction:
  functionName: request
  parameters:
    url: '/employees/{employee_id}'
    method: get
    args:
      - name: employee_id
        value: $.inputs.employee_id
        in: path
      - name: fields
        value: '{{$.inputs.fields ?? "all"}}'
        in: query
      - name: Content-Type
        value: application/json
        in: headers
```

| Parameter             | Type    | Description                                    |
| --------------------- | ------- | ---------------------------------------------- |
| `baseUrl`             | string  | Override connector baseUrl                     |
| `url`                 | string  | Endpoint path (appended to baseUrl)            |
| `method`              | enum    | `get`, `post`, `put`, `patch`, `delete`        |
| `authorization`       | object  | Override connector authentication              |
| `args`                | array   | Request parameters                             |
| `response.collection` | boolean | Whether response is an array (default: `true`) |
| `response.dataKey`    | string  | Extract data from this key in response         |
| `response.indexField` | string  | Field to use as index for keyed results        |
| `customErrors`        | array   | Remap provider error responses (see below)     |

#### Args Location (`in`)

| Value     | Description                           |
| --------- | ------------------------------------- |
| `path`    | URL path parameter: `/employees/{id}` |
| `query`   | Query string: `?field=value`          |
| `body`    | Request body (JSON)                   |
| `headers` | HTTP header                           |

<Accordion title="Request construction internals">
  The runtime builds requests as follows:

  1. **URL:** `baseUrl` + `url` with path params interpolated
  2. **Query:** All `in: query` args joined with `&`
  3. **Body:** All `in: body` args merged into JSON object
  4. **Headers:** Connector authentication headers + custom headers + `in: headers` args

  **Path parameter example:**

  ```yaml theme={null}
  url: '/employees/{employee_id}/time-off/{request_id}'
  args:
    - name: employee_id
      value: $.inputs.employee_id
      in: path
    - name: request_id
      value: $.inputs.request_id
      in: path
  ```

  Resolves to: `/employees/123/time-off/456`

  **Body construction:**

  ```yaml theme={null}
  args:
    - name: firstName
      value: $.inputs.first_name
      in: body
    - name: lastName
      value: $.inputs.last_name
      in: body
  ```

  Sends: `{ "firstName": "John", "lastName": "Doe" }`
</Accordion>

<Accordion title="Custom error handling">
  Use `customErrors` to remap provider error responses. This is useful for:

  * GraphQL APIs that return errors with 200 status codes
  * Provider-specific error formats that need normalization
  * Custom error messages for better AI agent understanding

  ```yaml theme={null}
  stepFunction:
    functionName: request
    parameters:
      url: /graphql
      method: post
      customErrors:
        - receivedStatus: 200
          targetStatus: 400
          condition: '{{present(data.errors)}}'
          message: GraphQL query failed
        - receivedStatus: 404
          targetStatus: 400
          message: Resource not found
        - receivedStatus: 200
          targetStatus: 401
          condition: '{{data.ok == false}}'
          message: '{{data.error}}'
  ```

  **customErrors properties:**

  | Property         | Type   | Description                                                                                                                                                                                                                                                                   |
  | ---------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `receivedStatus` | number | HTTP status to match from provider                                                                                                                                                                                                                                            |
  | `targetStatus`   | number | HTTP status to return instead                                                                                                                                                                                                                                                 |
  | `condition`      | string | JEXL expression (only trigger error if true)                                                                                                                                                                                                                                  |
  | `message`        | string | Error message. Pass a plain string for a static message, or a JEXL expression like `{{data.error}}` to pull the value from the response body. If the expression resolves to null or undefined, the message falls back to the standard HTTP status text (e.g. `Unauthorized`). |

  The `condition` property is useful for GraphQL APIs where errors are returned in the response body with a 200 status code.
</Accordion>

### `paginated_request`

**Impact:** ⚙️ Runtime

Automatically handle pagination for list endpoints.

```yaml theme={null}
stepFunction:
  functionName: paginated_request
  parameters:
    url: '/employees'
    method: get
    args:
      - name: page_size
        value: '{{$.inputs.page_size ?? 25}}'
        in: query
    pagination:
      type: cursor
      request:
        cursor_field: cursor
        cursor_position: query
      response:
        cursor_path: $.next_cursor
        data_path: $.employees
```

#### Pagination Types

```yaml theme={null}
# Cursor-based
pagination:
  type: cursor
  request:
    cursor_field: cursor
    cursor_position: query
  response:
    cursor_path: $.next_cursor
    data_path: $.data

# Offset-based
pagination:
  type: offset
  request:
    offset_field: offset
    limit_field: limit
    offset_position: query
  response:
    data_path: $.results
    total_path: $.total_count

# Page number
pagination:
  type: page_number
  request:
    page_field: page
    page_position: query
  response:
    data_path: $.items
    total_pages_path: $.total_pages
```

<Accordion title="Pagination handling mechanics">
  The runtime handles pagination automatically:

  **Cursor pagination:**

  1. Make initial request without cursor
  2. Read `cursor_path` from response
  3. If cursor exists, make next request with cursor in `cursor_position`
  4. Repeat until no cursor returned
  5. Aggregate all `data_path` results

  **Result format:**

  ```json theme={null}
  {
    "data": [...all records...],
    "next": "cursor_for_next_page_or_null"
  }
  ```

  **MCP behavior:** When an agent calls a list action, they receive the first page. The `next` cursor allows fetching subsequent pages by passing it back as an input parameter.
</Accordion>

### `map_fields`

**Impact:** ⚙️ Runtime

Transform data between formats.

```yaml theme={null}
stepFunction:
  functionName: map_fields
  version: '2'
  parameters:
    fields:
      - targetFieldKey: id
        expression: $.employeeId
        type: string
      - targetFieldKey: full_name
        expression: $.firstName + ' ' + $.lastName
        type: string
      - targetFieldKey: is_active
        expression: $.status == 'Active'
        type: boolean
      - targetFieldKey: hire_date
        expression: $.hireDate
        type: datetime_string
    dataSource: $.steps.fetch_employees.output.data
```

| Parameter                 | Type     | Description              |
| ------------------------- | -------- | ------------------------ |
| `fields`                  | array    | Mapping definitions      |
| `dataSource`              | JSONPath | Source data to transform |
| `fields[].targetFieldKey` | string   | Output field name        |
| `fields[].expression`     | JEXL     | Value expression         |
| `fields[].type`           | enum     | Output type              |

<Accordion title="Field mapping expressions">
  Expressions support JEXL syntax with the source object as context:

  **Simple path:**

  ```yaml theme={null}
  expression: $.employeeId
  ```

  **Concatenation:**

  ```yaml theme={null}
  expression: $.firstName + ' ' + $.lastName
  ```

  **Conditional:**

  ```yaml theme={null}
  expression: $.status == 'Active' ? 'employed' : 'terminated'
  ```

  **Null coalescing:**

  ```yaml theme={null}
  expression: $.middleName ?? ''
  ```

  **Nested access:**

  ```yaml theme={null}
  expression: $.department.name
  ```

  **Array operations:**

  ```yaml theme={null}
  expression: $.tags|join(',')
  ```

  **Type coercion:**
  The `type` field ensures output matches expected type:

  * `string` - Converts to string
  * `number` - Converts to number
  * `boolean` - Converts to boolean
  * `datetime_string` - Formats as ISO 8601
</Accordion>

### `group_data`

**Impact:** ⚙️ Runtime

Group array data by a field.

```yaml theme={null}
stepFunction:
  functionName: group_data
  parameters:
    groupByField: department_id
    dataSource: $.steps.fetch_employees.output.data
```

<Accordion title="Group data usage">
  Transforms flat array into grouped structure:

  **Input:**

  ```json theme={null}
  [
    { "name": "John", "department_id": "eng" },
    { "name": "Jane", "department_id": "eng" },
    { "name": "Bob", "department_id": "sales" }
  ]
  ```

  **Output:**

  ```json theme={null}
  {
    "eng": [
      { "name": "John", "department_id": "eng" },
      { "name": "Jane", "department_id": "eng" }
    ],
    "sales": [
      { "name": "Bob", "department_id": "sales" }
    ]
  }
  ```

  Useful for restructuring provider responses into more usable formats.
</Accordion>

### `typecast`

**Impact:** ⚙️ Runtime

Convert data types.

```yaml theme={null}
stepFunction:
  functionName: typecast
  parameters:
    type: number
    dataSource: $.steps.previous.output.data.count
```

| Type      | Description                 |
| --------- | --------------------------- |
| `string`  | Convert to string           |
| `number`  | Parse as number             |
| `boolean` | Convert to boolean          |
| `json`    | Parse JSON string to object |

### `soap_request`

**Impact:** ⚙️ Runtime

Make SOAP API requests. Used primarily for enterprise providers like Workday.

```yaml theme={null}
stepFunction:
  functionName: soap_request
  parameters:
    baseUrl: 'https://${credentials.workday_host}'
    url: '/ccx/service/${credentials.tenant}/Human_Resources'
    method: post
    authorization:
      type: bearer
      token: ${credentials.accessToken}
      includeBearer: true
    soapOperation: Get_Workers
    useSoapContext: false
    namespaces:
      - namespaceIdentifier: bsvc
        namespace: 'urn:com.workday/bsvc'
    args:
      - name: '@_bsvc:version'
        in: body
        value: v45.1
      - name: bsvc:Request_Criteria
        in: body
        value:
          'bsvc:Transaction_Log_Criteria_Data':
            - 'bsvc:Transaction_Date_Range_Data':
                'bsvc:Updated_From': '{{$.inputs.updated_after}}'
```

| Parameter        | Type    | Description                         |
| ---------------- | ------- | ----------------------------------- |
| `baseUrl`        | string  | SOAP service base URL               |
| `url`            | string  | SOAP endpoint path                  |
| `method`         | enum    | HTTP method (typically `post`)      |
| `authorization`  | object  | Auth config (same as `request`)     |
| `soapOperation`  | string  | SOAP operation name                 |
| `useSoapContext` | boolean | Use SOAP context (default: `false`) |
| `namespaces`     | array   | XML namespace definitions           |
| `args`           | array   | Request parameters                  |

<Accordion title="SOAP namespace configuration">
  Namespaces map XML prefixes to URIs:

  ```yaml theme={null}
  namespaces:
    - namespaceIdentifier: bsvc
      namespace: 'urn:com.workday/bsvc'
    - namespaceIdentifier: wd
      namespace: 'urn:com.workday/bsvc/wd'
  ```

  Reference namespaces in field names with the identifier prefix:

  ```yaml theme={null}
  args:
    - name: 'bsvc:Worker_Reference'
      in: body
      value:
        'bsvc:ID':
          '@_bsvc:type': 'Employee_ID'
          '#text': '{{$.inputs.employee_id}}'
  ```

  **XML attribute syntax:** Use `@_` prefix for XML attributes and `#text` for text content.
</Accordion>

### `static_values`

**Impact:** ⚙️ Runtime

Return predefined static values without making an API request. Useful for enum lookups or constant data.

```yaml theme={null}
stepFunction:
  functionName: static_values
  parameters:
    values:
      - id: question
        name: Question
      - id: incident
        name: Incident
      - id: problem
        name: Problem
      - id: task
        name: Task
```

| Parameter | Type  | Description                       |
| --------- | ----- | --------------------------------- |
| `values`  | array | Array of static objects to return |

<Accordion title="Static values use cases">
  Use `static_values` when:

  1. **Provider doesn't have an API endpoint** for enum values:

  ```yaml theme={null}
  # Ticket types are hardcoded in Zendesk
  stepFunction:
    functionName: static_values
    parameters:
      values:
        - id: question
          name: Question
        - id: incident
          name: Incident
  ```

  2. **Combining with dynamic data** via multi-step actions:

  ```yaml theme={null}
  steps:
    static_types:
      stepFunction:
        functionName: static_values
        parameters:
          values:
            - id: default
              name: Default Type

    api_types:
      stepFunction:
        functionName: request
        parameters:
          url: /custom-types

    merge_types:
      stepFunction:
        functionName: merge_collections
        parameters:
          collections:
            - $.steps.static_types.output.data
            - $.steps.api_types.output.data
  ```
</Accordion>

### `merge_collections`

**Impact:** ⚙️ Runtime

Merge multiple data collections into a single array.

```yaml theme={null}
stepFunction:
  functionName: merge_collections
  parameters:
    collections:
      - $.steps.static_defaults.output.data
      - $.steps.api_results.output.data
```

| Parameter     | Type  | Description                            |
| ------------- | ----- | -------------------------------------- |
| `collections` | array | JSONPath references to arrays to merge |

<Accordion title="Merge collections example">
  Combine static defaults with API-fetched data:

  ```yaml theme={null}
  steps:
    default_statuses:
      stepFunction:
        functionName: static_values
        parameters:
          values:
            - id: open
              name: Open
            - id: closed
              name: Closed

    custom_statuses:
      stepFunction:
        functionName: request
        parameters:
          url: /custom-statuses

    all_statuses:
      stepFunction:
        functionName: merge_collections
        parameters:
          collections:
            - $.steps.default_statuses.output.data
            - $.steps.custom_statuses.output.data

  result:
    outputDataPath: $.steps.all_statuses.output.data
  ```

  Collections are merged in order—items from the first collection appear first in the result.
</Accordion>

### `code_execution_lambda`

**Impact:** ⚙️ Runtime

Invoke an AWS Lambda function and use the response as the step output. Useful for offloading custom transformations, provider-specific logic, or computation that doesn't fit other step functions.

```yaml theme={null}
stepFunction:
  functionName: code_execution_lambda
  parameters:
    lambdaName: my-lambda-function
    region: us-east-1
    invocationType: RequestResponse
    args:
      - name: userId
        value: $.inputs.user_id
      - name: count
        value: $.inputs.count
```

| Parameter          | Type   | Description                                                                |
| ------------------ | ------ | -------------------------------------------------------------------------- |
| `lambdaName`       | string | Name or ARN of the Lambda function to invoke                               |
| `region`           | string | AWS region of the Lambda function (optional)                               |
| `endpoint`         | string | Custom Lambda endpoint URL (optional)                                      |
| `invocationType`   | enum   | `RequestResponse`, `Event`, or `DryRun` (default: `RequestResponse`)       |
| `qualifier`        | string | Function version or alias (optional)                                       |
| `args`             | array  | Key/value pairs combined into the JSON payload sent to the Lambda function |
| `args[].name`      | string | Payload field name                                                         |
| `args[].value`     | any    | Payload field value (supports expressions)                                 |
| `args[].condition` | string | JEXL expression — only include this arg when true (optional)               |
| `dataKey`          | string | Extract data from this key in the Lambda response (optional)               |

#### Invocation Types

| Value             | Description                                                       |
| ----------------- | ----------------------------------------------------------------- |
| `RequestResponse` | Synchronous — wait for the Lambda to return a response            |
| `Event`           | Asynchronous — fire-and-forget, no response body returned         |
| `DryRun`          | Validate parameters and permissions without invoking the function |

#### Output

| Field             | Type   | Description                                                 |
| ----------------- | ------ | ----------------------------------------------------------- |
| `data`            | any    | Parsed Lambda response (after `dataKey` extraction if set)  |
| `raw`             | any    | Full Lambda response payload                                |
| `statusCode`      | number | HTTP status code returned by the Lambda invocation          |
| `message`         | string | Error or status message (optional)                          |
| `executedVersion` | string | Version of the Lambda function that was executed (optional) |

<Accordion title="Payload construction and response handling">
  The `args` array is assembled into a single JSON object and sent as the Lambda payload:

  **Input args:**

  ```yaml theme={null}
  args:
    - name: userId
      value: $.inputs.user_id
    - name: count
      value: $.inputs.count
  ```

  **Sent payload:**

  ```json theme={null}
  { "userId": "abc123", "count": 5 }
  ```

  **Conditional args:**

  ```yaml theme={null}
  args:
    - name: filter
      value: ${inputs.filter}
      condition: '{{present(inputs.filter)}}'
  ```

  **Extracting a nested response:**

  ```yaml theme={null}
  stepFunction:
    functionName: code_execution_lambda
    parameters:
      lambdaName: enrich-user
      region: us-east-1
      dataKey: result
      args:
        - name: userId
          value: ${inputs.user_id}
  ```

  If the Lambda returns `{ "result": { "score": 0.9 }, "meta": {...} }`, then `$.steps.{stepId}.output.data` is `{ "score": 0.9 }`.
</Accordion>

### Iterator Steps

**Impact:** ⚙️ Runtime

Iterator steps (foreach) execute step function(s) for each item in an array. Define by adding the `iterator` property to a step.

```yaml theme={null}
- stepId: get_employee_details
  description: Fetch detailed data for each employee
  iterator: $.steps.list_employees.output.data.employees[*].id
  stepFunction:
    functionName: request
    parameters:
      url: /employees/${iterator.item}
      method: get
```

| Property        | Type     | Description                                                                     |
| --------------- | -------- | ------------------------------------------------------------------------------- |
| `iterator`      | JSONPath | Expression that evaluates to an array                                           |
| `stepFunction`  | object   | Single function to execute per item                                             |
| `stepFunctions` | array    | Multiple functions to execute per item (mutually exclusive with `stepFunction`) |

<Accordion title="Iterator context variables">
  Inside an iterator step, these additional variables are available:

  | Variable             | Description                                                    |
  | -------------------- | -------------------------------------------------------------- |
  | `$.iterator.item`    | Current array element                                          |
  | `$.iterator.index`   | Current iteration index (0-based)                              |
  | `$.iterator.current` | Output from previous step function in `stepFunctions` sequence |
  | `${iterator.item}`   | String interpolation of current item (for URLs)                |

  **Multiple step functions per iteration:**

  ```yaml theme={null}
  - stepId: enrich_employees
    iterator: $.steps.list.output.data[*].id
    stepFunctions:
      - functionName: request
        parameters:
          url: /employees/${iterator.item}/details
          method: get
      - functionName: request
        parameters:
          url: /employees/${iterator.item}/compensation
          method: get
  ```

  Each iteration collects the final step function output. All iteration results are combined into `$.steps.{stepId}.output.data` as an array.
</Accordion>
