# Data Transformation

> Format text, calculate numbers, parse dates, and reshape lists directly inside your step fields without adding extra canvas steps.

When building workflows, incoming data is rarely in the exact format your downstream apps expect. Customer names might have accidental spaces, prices might lack tax or currency symbols, and timestamps might be in UTC instead of your local timezone.

Instead of cluttering your canvas with intermediate code or formatting steps, **Data transformation** lets you clean, calculate, and reshape data **directly inside any step field**.

---

## The Core Concept

Whenever you reference data from an earlier step (like `{{ 2.email }}` or `{{ 3.total }}`), you can attach a chain of transformations to it. The transformations execute in-memory right when the step reads the token, without creating intermediate canvas steps or spending extra step credits.

---

## How to Use Data Transformation

You can configure transformations visually or write them inline using pipe syntax.

### 1. Visual Configuration (Workflow Data Panel)

You can configure transformations visually without writing formulas or syntax:

### Open the Workflow Data Panel

Click into any step input field on your canvas to open the **Workflow data** drawer/modal (where you select dynamic variables).

### Switch to the Data Transformation Tab

In the Workflow data top bar, click the **Data transformation** tab (next to **Data select** and **Data flow**).

### Add Transformations to Step Variables

All variables mapped in the current step appear here. Click **Add transformation** on any variable to add operations (such as **Trim spaces**, **To lowercase**, **Times**, **Format as currency**, or **Format the date**).

### Live In-Memory Execution

The transformations run in memory when this step resolves the field. The transformed value is used in that field, while the earlier step's stored output stays unchanged. If later steps need the changed value as a reusable result, use a dedicated canvas step instead.

### 2. Fast Inline Pipe Syntax

If you prefer writing directly inside text inputs, append a pipe (`|`) and the operation name inside any variable token:

```text
{{ 2.email | trim | lower }}
{{ 3.subtotal | times:1.21 | format_currency:"$" }}
{{ 1.created_at | format_date:date_medium:Europe/Prague }}
```

---

## Everyday Examples

| Common Goal                      | Original Data            | Transformation                            | Result               |
| :------------------------------- | :----------------------- | :---------------------------------------- | :------------------- |
| **Clean up customer name**       | `"  john doe  "`         | `Trim spaces` + `Capitalise Each Word`    | `"John Doe"`         |
| **Extract email domain**         | `"alex@acme.com"`        | `Take the domain`                         | `"acme.com"`         |
| **Add 21% VAT and format price** | `100`                    | `Times: 1.21` + `Format as currency: "$"` | `"$121.00"`          |
| **Format phone number**          | `"777123456"`            | `Format a phone number: CZ`               | `"+420 777 123 456"` |
| **Human-readable date**          | `"2026-09-01T12:00:00Z"` | `Format the date: 13 Aug 2026`            | `"1 Sep 2026"`       |
| **Join list into text**          | `["Apple", "Banana"]`    | `Join items (comma + space)`              | `"Apple, Banana"`    |
| **Fallback for missing value**   | `null` or `""`           | `If empty, use: "Unknown"`                | `"Unknown"`          |

---

## Operation Catalogue

### Text & Clean Up Operations

Use these operations to clean user input, change capitalization, slice text, and extract specific tokens.

| Operation                     | Inline Syntax            | Description                                                                          | Example                                             |
| :---------------------------- | :----------------------- | :----------------------------------------------------------------------------------- | :-------------------------------------------------- |
| **Trim spaces**               | `\| trim`                | Removes leading and trailing whitespace.                                             | `"  hello  "` → `"hello"`                           |
| **lower case**                | `\| lower`               | Converts all letters to lowercase.                                                   | `"Hello World"` → `"hello world"`                   |
| **UPPER CASE**                | `\| upper`               | Converts all letters to uppercase.                                                   | `"Hello World"` → `"HELLO WORLD"`                   |
| **Capitalise Each Word**      | `\| capitalize`          | Capitalizes first letter of each word; preserves hyphens.                            | `"john doe"` → `"John Doe"`                         |
| **Take the text out of HTML** | `\| strip_html`          | Removes HTML tags while preserving line breaks and converting list items to bullets. | `"Hi"` → `"Hi"`                              |
| **Take the markdown out**     | `\| strip_markdown`      | Removes markdown headings, bolding, and links for plain text messaging.              | `"**Important**"` → `"Important"`                   |
| **Remove the code fence**     | `\| strip_fence`         | Unwraps outer ` ``` ` markdown code blocks from AI outputs.                          | `"```json\n{}\n```"` → `"{}"`                       |
| **Replace text**              | `\| replace:"old":"new"` | Replaces all occurrences of a search string.                                         | `"cat" \| replace:"cat":"dog"` → `"dog"`            |
| **Everything before**         | `\| before:"@"`          | Extracts text before the first occurrence of a delimiter.                            | `"user@glow.app"` → `"user"`                        |
| **Everything after**          | `\| after:"@"`           | Extracts text after the first occurrence of a delimiter.                             | `"user@glow.app"` → `"glow.app"`                    |
| **First line only**           | `\| first_line`          | Takes only the first non-empty line of text.                                         | `"Line 1\nLine 2"` → `"Line 1"`                     |
| **Shorten to**                | `\| limit:100`           | Truncates text after a specified character count.                                    | `"Long text" \| limit:4` → `"Long"`                 |
| **Split on**                  | `\| split:comma`         | Splits text into a true list (by comma, space, newline, or tab).                     | `"a, b" \| split:comma` → `["a", "b"]`              |
| **Take the domain**           | `\| extract_domain`      | Extracts clean domain from URL or email (removes `www.` and protocols).              | `"https://glow.app"` → `"glow.app"`                 |
| **Take the email address**    | `\| extract_email`       | Finds and extracts the first valid email address from raw text.                      | `"Contact support@glow.app"` → `"support@glow.app"` |
| **Take the number**           | `\| extract_number`      | Extracts first numeric sequence (preserves leading zeros for SKUs).                  | `"Order #0042"` → `"0042"`                          |
| **Take the link**             | `\| extract_url`         | Finds and extracts the first web address from text.                                  | `"Visit https://glow.app"` → `"https://glow.app"`   |
| **Make safe for JSON**        | `\| escape_json`         | Escapes quotes and newlines so text can safely sit inside raw JSON.                  | `"A \"quote\""` → `"A \\\"quote\\\""`               |
| **Make safe for web address** | `\| url_encode`          | Encodes special characters for URL parameters.                                       | `"Hello World"` → `"Hello%20World"`                 |
| **Count characters / words**  | `\| count_characters`    | Returns the total character or word count as a number.                               | `"Hello"` → `5`                                     |

</Tabs.Tab>

<Tabs.Tab>

### Numbers & Arithmetic

Perform calculations, rounding, currency formatting, and phone number formatting.

| Operation                     | Inline Syntax                      | Description                                                           | Example                              |
| :---------------------------- | :--------------------------------- | :-------------------------------------------------------------------- | :----------------------------------- |
| **Plus…**                     | `\| plus:10`                       | Adds a number.                                                        | `15 \| plus:5` → `20`                |
| **Minus…**                    | `\| minus:5`                       | Subtracts a number.                                                   | `100 \| minus:15` → `85`             |
| **Times…**                    | `\| times:1.21`                    | Multiplies by a factor (e.g. calculate 21% VAT).                      | `50 \| times:1.21` → `60.5`          |
| **Divided by…**               | `\| divided_by:100`                | Divides by a number (e.g. cents to dollars).                          | `2500 \| divided_by:100` → `25`      |
| **Round**                     | `\| round:2`                       | Rounds to a fixed number of decimal places.                           | `3.14159 \| round:2` → `"3.14"`      |
| **Round up** / **Round down** | `\| round_up`                      | Rounds to the nearest ceiling or floor integer.                       | `3.2 \| round_up` → `4`              |
| **Drop the minus sign**       | `\| absolute`                      | Returns positive absolute magnitude (`Math.abs`).                     | `-42` → `42`                         |
| **Cut off the decimals**      | `\| truncate:2`                    | Truncates decimal digits without rounding.                            | `12.349 \| truncate:2` → `12.34`     |
| **Format for reading**        | `\| number_format:2:sep_comma_dot` | Formats numbers with thousands separators (`1,234.56` or `1 234,56`). | `1234567.89` → `"1,234,567.89"`      |
| **Format as currency**        | `\| format_currency:"$"`           | Prepends currency symbol and applies number formatting.               | `1499.5` → `"$1,499.50"`             |
| **Format as a percent**       | `\| format_percent:1`              | Multiplies by 100 and appends `%`.                                    | `0.125` → `"12.5 %"`                 |
| **Format a phone number**     | `\| format_phone:CZ:international` | Normalizes phone strings with country prefix assumptions.             | `"777123456"` → `"+420 777 123 456"` |

</Tabs.Tab>

<Tabs.Tab>

### Dates & Timezones

Parse, shift, and format dates across global timezones.

All date operations accept an optional IANA timezone (such as `Europe/Prague`, `America/New_York`, or `UTC`). If omitted, **UTC** is used.

| Operation                   | Inline Syntax                              | Description                                                                                          | Example                                    |
| :-------------------------- | :----------------------------------------- | :--------------------------------------------------------------------------------------------------- | :----------------------------------------- |
| **Format the date**         | `\| format_date:date_medium:Europe/Prague` | Formats date into readable presets (`date_medium`, `date_iso`, `date_day_first`, `date_time`, etc.). | `"2026-09-01T10:00:00Z"` → `"1 Sep 2026"`  |
| **Add time**                | `\| date_add:14:days`                      | Shifts date forward or backward (`days`, `weeks`, `months`, `hours`).                                | Add trial period days to signup date.      |
| **Time from now**           | `\| date_diff:days`                        | Calculates difference between now and target date.                                                   | Days remaining until subscription ends.    |
| **Start of** / **End of**   | `\| date_start_of:month`                   | Snaps timestamp to first (`00:00:00`) or final (`23:59:59.999`) millisecond of period.               | Snapping invoices to end of billing month. |
| **Take a part**             | `\| date_part:month_name`                  | Extracts specific component (`year`, `month_name`, `weekday_name`, `hour`).                          | `"2026-08-24"` → `"August"`                |
| **Read a date written as…** | `\| parse_date:day_first`                  | Parses non-standard date strings (`24/08/2026`, Unix seconds/ms) into ISO format.                    | Standardizing user-typed dates.            |

</Tabs.Tab>

<Tabs.Tab>

### Lists & Arrays

Extract, slice, join, and aggregate list data directly inside field tokens.

| Operation                      | Inline Syntax                | Description                                                                      | Returns     |
| :----------------------------- | :--------------------------- | :------------------------------------------------------------------------------- | :---------- |
| **First item** / **Last item** | `\| first` / `\| last`       | Takes the first or last element of a list.                                       | Single item |
| **Item number…**               | `\| item_at:3`               | Takes an item by its 1-based index position.                                     | Single item |
| **Take each item's field**     | `\| pluck:email`             | Extracts one specific property across all records in a list.                     | List        |
| **Join items**                 | `\| join:comma_space`        | Turns a list of text into a single string separated by commas, spaces, or lines. | Text        |
| **Keep the first…**            | `\| keep_first:5`            | Slices list to keep only the first _N_ items.                                    | List        |
| **Remove duplicates**          | `\| remove_duplicates`       | Keeps only unique elements, preserving order.                                    | List        |
| **Reverse the order**          | `\| reverse`                 | Reverses list ordering.                                                          | List        |
| **Sort**                       | `\| sort:ascending:name`     | Sorts items alphabetically, numerically, or by a record field.                   | List        |
| **Total** / **Average**        | `\| total` / `\| average`    | Calculates sum or mean across a list of numbers.                                 | Number      |
| **Smallest** / **Largest**     | `\| smallest` / `\| largest` | Returns the minimum or maximum numeric value.                                    | Number      |

</Tabs.Tab>

</Tabs>

---

## Fallback Values (`If empty`)

If an upstream field might be missing, `null`, or an empty string, append **`If empty, use`** (or `| default:"value"`) to provide a fallback:

```text
{{ 3.company | default:"Individual / Self-Employed" }}
```

> **Safe Defaults:** The `default` operation only replaces missing values or
> empty strings. Valid values such as `0` or `false` are preserved and will
> never be overwritten.

---

## Choosing Between In-Field Transforms and Canvas Steps

| Use In-Field Data Transformation                                                            | Use Dedicated Canvas Steps                                                                                                                     |
| :------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------- |
| **Formatting & Cleaning:** Lowercase text, trim spaces, extract domains, format currencies. | **Branching & Logic:** Routing workflows based on complex condition branches ([Conditions](/build/action-steps/conditions)).                   |
| **Direct Field Aggregation:** Plucking emails from a list and joining with commas.          | **Looping Over API Actions:** Calling an external API once per item ([Repeater](/build/action-steps/loops/repeater)).                          |
| **Dataset Size:** Up to **10,000 items**.                                                   | **Large Scale Datasets:** Sorting and filtering 100,000+ items ([Sort](/build/action-steps/sort), [Filter](/build/action-steps/filter-items)). |

## What's Next?

- 👉 **[Kinds of Data →](/build/core-concepts/data-types)**: Learn how records, lists, and files move between steps.
- **[Variable Reference Syntax](/reference/variable-syntax)**: Master expressions, nested paths, and system variables.
- **[Repeater Step](/build/action-steps/loops/repeater)**: Loop through arrays and execute actions per item.
