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

# Multi-value fields

> How multi-select values behave across calculations, Execute Script, Repeat for Each, and Send API Request — with the working pattern for each need.

Multi-select fields (fields whose type is Multi-Select on a layout, stored internally as `MULTI_PICKLIST`) hold several selected options in a single value. That shape works cleanly on the record itself, but it behaves differently in each place you might reference it downstream — calculations, an Execute Script action, a Repeat for Each loop, or a Send API Request body. This page names the shape, says which action to pick for each need, and gives the working pattern.

If you're here because of the specific error `Invalid Type Error at line 1, position N: MULTI_PICKLIST`, jump to [In calculations](#in-calculations).

## The two shapes you'll encounter

**Multi-picklist fields.** A field configured as Multi-Select on an Element or Table layout. A single cell holds several selected options. Elementum stores the underlying type as `MULTI_PICKLIST`.

**Multi-value text from Data Mine.** When a [Data Mine](/data/data-mining) trigger fires, a multi-value text column in the payload serializes as an array of objects like `[{"value":"WD","type":"TEXT"}, ...]` rather than plain text. This is a different problem from a multi-picklist field — it's a text or JSON payload whose *value* happens to be array-shaped. See [Flattening a Data Mine array payload into plain text](/data/calculations-troubleshooting#flattening-a-data-mine-array-payload-into-plain-text) for the specifics; the rest of this page is about multi-picklist fields on a record.

## Which action to use

Pick the action that matches what you're trying to do with the selections. Every row below has a working pattern later on this page.

| I want to…                                                                                         | Use                                                                                  | Notes                                                                                                        |
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| Loop over each selection and do something with it (send a message per, create a record per)        | [Repeat for Each](#in-repeat-for-each) pointed at the field                          | Simplest option. No script needed.                                                                           |
| Aggregate the selections into a single string, count, or other summary value                       | [Execute Script](#aggregating-into-a-comma-separated-string)                         | Calculations can't aggregate a multi-picklist directly.                                                      |
| Include the selections in a JSON body for an outbound API call                                     | [Execute Script](#passing-multi-select-values-into-a-json-api-body) → **JSON Input** | Build the payload in a script, then pass the stringified result into the Send API Request action.            |
| Produce a delimited string inside a calculated column on a Table or a formula on an Element layout | Not currently possible                                                               | See [In calculations](#in-calculations). Do the work in Execute Script and write the result back to a field. |

## In Execute Script

Multi-select inputs come through as an **array of objects with named properties**, not an array of primitive strings. What the object exposes depends on how the multi-select field is configured — a static option list and a data-source-backed multi-select map into the Inputs panel differently.

### The runtime shape

Given a multi-select field mapped as an input parameter, the value at `input.parameters.<parameterName>` is an array where each item is an object. The object's properties are the ones you expose in the Inputs panel:

```javascript theme={null}
input.parameters.tags = [
  { label: "High priority" },
  { label: "Needs review" }
];

input.parameters.addresses = [
  { id: "addr_123", title: "HQ", street: "1 Main St" },
  { id: "addr_456", title: "Warehouse", street: "500 Depot Rd" }
];
```

The value is an array in every expected case:

* **Zero selections** — an empty array `[]`.
* **Exactly one selection** — an array of length 1, not a bare object. Call `.map()` unconditionally rather than normalizing with `Array.isArray(x) ? x : [x]`.
* **Several selections** — one item per selection.

Because the value can still arrive as `null` in edge cases, treat every list input as though it might be null or missing. The `?? []` pattern is safe whether the input is `null`, missing, or already an array:

```javascript theme={null}
const items = input.parameters.tags ?? [];
```

### Configuring the input mapping

Every multi-select input in the **Script Configuration → Inputs** panel starts with a **Parameter** name — that's the JavaScript variable you'll reference through `input.parameters.<parameterName>`. The rows nested underneath control which properties of each selected item are exposed inside the script.

Two behaviors, depending on how the source field is configured:

* **Static option list.** The Inputs panel shows one locked row with `label` in both **Select Field** and **Parameter**. No **+ Add Field** control. Each item in the resulting array has exactly one property — `label`. This applies to static single-select fields too (with the same locked `label` behavior), but the value comes through as an array only for multi-select.
* **Data-source-backed multi-select** (options come from records of an Object, an API-powered dropdown, or a similar dynamic source). The Inputs panel shows a **+ Add Field** control. Each field you add has three inputs:

| Input            | Purpose                                                                                                                                                    |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Select Field** | Choose which property on the underlying source object to expose.                                                                                           |
| **Parameter**    | The JavaScript property name used inside `input.parameters.<parameterName>[i].<Parameter>`. Rename freely — you're not stuck with the source field's name. |
| **Test value**   | Sandbox value used when clicking **Execute** in the script editor. Doesn't affect production runs.                                                         |

Only the properties you expose here are available to the script. If a script needs an additional field from the source object later, come back to the Inputs panel and add it — the Output Schema regenerates when you next click **Execute**.

### Iterating each selection

Loop over the array and reach into whichever property you exposed. For a static multi-select, that property is always `label`:

```javascript theme={null}
const labels = (input.parameters.tags ?? []).map(tag => tag.label);

return { labels };
```

For a data-source-backed multi-select, use the **Parameter** name you configured for each field:

```javascript theme={null}
const { addresses } = input.parameters;

const streetLines = (addresses ?? [])
  .map(a => `${a.title}: ${a.street}`)
  .join('\n');

return { streetLines };
```

If you only need to act on each selection in a downstream action (create a record per, send an email per, and so on), skip the script entirely and use [Repeat for Each](#in-repeat-for-each) directly on the field. Execute Script is the right choice when you need to *transform* the collection — aggregate, filter, reshape — before handing it off.

### Aggregating into a comma-separated string

There is no calculation function that aggregates a multi-picklist into a delimited string (see [In calculations](#in-calculations)). The working pattern is Execute Script:

```javascript theme={null}
const csv = (input.parameters.tags ?? [])
  .map(tag => tag.label)
  .join(', ');

return { csv };
```

Return `csv` from the script and reference it in downstream actions — for example, mapping it into a text field with **Update Record Fields**, or into a message body in **Post Comment** or **Send Email Notification**.

For a data-source-backed multi-select, swap the `.map(tag => tag.label)` for whichever property you want to aggregate:

```javascript theme={null}
const titles = (input.parameters.addresses ?? [])
  .map(a => a.title)
  .join(', ');

return { titles };
```

### Passing multi-select values into a JSON API body

The **JSON Input** field on the Send API Request action expects a plain JSON string, not a structured object. Build the payload in Execute Script, `JSON.stringify()` it, and map the resulting string into **JSON Input**. This pattern decouples payload construction from the API action — you can swap the script's inputs without touching the API step, and reuse the same body across several Send API Request actions.

```javascript theme={null}
const tags = (input.parameters.tags ?? []).map(tag => tag.label);

return {
  payload: JSON.stringify({
    ticketId: input.parameters.ticketId,
    tags
  })
};
```

Then map `payload` into the Send API Request action's **JSON Input** field. On the wire the request body becomes:

```json theme={null}
{
  "ticketId": "T-1042",
  "tags": ["High priority", "Needs review"]
}
```

What the receiving API sees depends entirely on how you structure the object before stringifying. Common variants:

* **JSON array of strings** — `{ tags: (input.parameters.tags ?? []).map(t => t.label) }`
* **JSON array of full objects** — `{ tags: input.parameters.tags ?? [] }` (each item keeps every property you exposed in the Inputs panel)
* **Comma-separated string** — `{ tags: (input.parameters.tags ?? []).map(t => t.label).join(', ') }`

For the general `JSON.stringify` requirement on this action and the `failed to convert input data to params` error you'll see if you skip it, see the [Send API Request](/workflows/automation-actions-reference#send-api-request) reference.

## In Repeat for Each

The Repeat for Each action accepts a multi-picklist field directly as its **Collection**. You do not need to bridge through Execute Script or a Search Records action first.

Inside the loop, each property you exposed on the selected item becomes a separate value reference in the picker (type `$` in any inner action's fields). For a static multi-select, that's a single `Label` entry. For a data-source-backed multi-select, it's one entry per exposed field, prefixed with the parameter name — `Addresses - Title`, `Addresses - ID`, and so on. Two loop-level helpers are always available:

* **Is First Item** — checkbox, `true` on the first iteration.
* **Is Last Item** — checkbox, `true` on the last iteration.

**When to prefer Repeat for Each over Execute Script:**

* You want to run one action (or a small block of actions) per selection without transforming the collection.
* The per-selection logic is visible in the automation builder rather than tucked inside JavaScript — easier for reviewers and future maintainers to read.
* No aggregation is needed. If you need a totalled number or a joined string across selections, use Execute Script instead.

## In calculations

There is **no supported calculation pattern that aggregates a multi-picklist field into a delimited string, or otherwise operates on all of its selections at once**. `STRING_AGG` and `STRING_AGG_UNIQUE` accept single-value text supplied through a related-field aggregation only, and passing a `MULTI_PICKLIST` field to either raises:

```
Invalid Type Error at line 1, position N: MULTI_PICKLIST
```

This applies everywhere calculations run — calculated columns on a [Table](/data/tables), formulas on an [Element](/workflows/layouts) layout, and the [Run Calculation](/workflows/automation-actions-reference#run-calculation) action.

**What to do instead.** If your ultimate need is a text value derived from the selections (a comma-separated summary, a count, a joined description), do the work in [Execute Script](#aggregating-into-a-comma-separated-string) and, if the value needs to live on a record, write it back with **Update Record Fields**. If your need is per-selection processing, use [Repeat for Each](#in-repeat-for-each) directly.

For the full error catalog and the analogous Data Mine limitation, see [Calculations troubleshooting → Multi-value and array-shaped values](/data/calculations-troubleshooting#multi-value-and-array-shaped-values).

## Related documentation

* **[Execute Script action](/workflows/automation-actions-reference#execute-script)** — full reference for input mapping, Output Schema, and the runtime environment.
* **[Repeat for Each action](/workflows/automation-actions-reference#repeat-for-each)** — collection loops in the automation builder.
* **[Send API Request action](/workflows/automation-actions-reference#send-api-request)** — request body shapes, authorization, and the `JSON.stringify` requirement.
* **[Calculations troubleshooting](/data/calculations-troubleshooting)** — the multi-value and Data Mine array sections cover the calculation-side limitations in more depth.
* **[Data Mine](/data/data-mining)** — where the array-of-objects text payload shape comes from.
