# Flatten

Explode the list at a field path into one record per element, keeping the parent's other fields.

## When to use it

Expand a list into separate records while preserving the parent’s other fields. Use it before row-based processing of an array. Check the output field choice to avoid replacing another meaningful field on the parent.

## Add and configure

1. Add **Flatten** from the workflow step picker.
2. Give the step a recognisable name and connect its required inputs.
3. Set the options below. Nested names identify fields within an object or a list item.
4. Validate the recipe, inspect the test result, then run it on the intended data.

The step identifier used in the editor’s code view is `flatten`.

## Settings

| Setting | Accepted value | Required | Default | What it controls |
| --- | --- | --- | --- | --- |
| Index Field (`index_field`) | string; minimum characters 1 or null | No | null | Field that receives each element's 0-based position in its list. |
| Into (`into`) | string; minimum characters 1 or null | No | null | Top-level field that receives each element; the list itself is dropped. Omitted: the element replaces the list where it was. |
| Keep Empty (`keep_empty`) | boolean | No | false | Keep a record whose list is missing, null or empty as one row with a null element, instead of dropping it. |
| Max Output Records (`max_output_records`) | integer; minimum 1; maximum 500000 | No | 100000 | The step fails (`output_cap`) rather than emit more rows than this. |
| Path (`path`) | string | Yes | Not specified | Plain field path of the list to explode (e.g. `$.items`, `$.order.lines`; a trailing `[*]` is accepted). Nested lists are flattened by chaining steps. |
| Spread (`spread`) | boolean | No | false | Merge each (object) element's fields into the row instead — an element field wins over a parent field of the same name; the list itself is dropped. |

## Inputs and outputs

| Direction | Port | Content | Required | Multiple records |
| --- | --- | --- | --- | --- |
| Inputs | `in` | records | Yes | Yes |
| Outputs | `out` | records | Yes | Yes |

## Effects, testing, and recovery

Transforms or passes records without a declared external side effect.

The declared preview behaviour is to execute this step. Reads, processing, or model usage can therefore be real. Read [dry runs](/docs/workflows/dry-runs) before using a preview with live connections.

No duplicate-prevention key is declared for this step. The step declares safe replay for its supported recovery path.

## Example: One row per order line

Step settings:

```json
{
  "into": "line",
  "path": "$.lines"
}
```

Input records:

```json
[
  {
    "lines": [
      {
        "sku": "a"
      },
      {
        "sku": "b"
      }
    ],
    "order": "o1"
  }
]
```

Expected output for this example:

```json
[
  {
    "line": {
      "sku": "a"
    },
    "order": "o1"
  },
  {
    "line": {
      "sku": "b"
    },
    "order": "o1"
  }
]
```

## Example: Unwrap search hits into rows, with their rank

Step settings:

```json
{
  "index_field": "rank",
  "path": "$.matches",
  "spread": true
}
```

Input records:

```json
[
  {
    "matches": [
      {
        "text": "first"
      },
      {
        "text": "second"
      }
    ],
    "query": "q"
  }
]
```

Expected output for this example:

```json
[
  {
    "query": "q",
    "rank": 0,
    "text": "first"
  },
  {
    "query": "q",
    "rank": 1,
    "text": "second"
  }
]
```

## Example: Explode tags in place, keeping untagged posts

Step settings:

```json
{
  "keep_empty": true,
  "path": "$.tags"
}
```

Input records:

```json
[
  {
    "post": 1,
    "tags": [
      "red",
      "blue"
    ]
  },
  {
    "post": 2,
    "tags": []
  }
]
```

Expected output for this example:

```json
[
  {
    "post": 1,
    "tags": "red"
  },
  {
    "post": 1,
    "tags": "blue"
  },
  {
    "post": 2,
    "tags": null
  }
]
```

## Check the result

Inspect the selected run’s status, counts, errors, and output records. An empty collection can be a valid result, and a completed step can contain per-record failures where the step defines that behaviour. Check the next step’s expected record shape before publishing or delivering the result.
