# Library Reference

## Expression Engine — Quick Reference Cheat Sheet

**For Customer v2 flow builders.** Use expressions to insert live data — customer names, order details, formatted prices — into your Say, Agent, and Menu prompts. Write `{{expression}}` in any text field and the engine replaces it with the real value at runtime.

***

### 1. How It Works&#x20;

You call an API, it returns data, and you use `{{double braces}}` to inject that data into your prompts.

**Example:** Your flow calls a customer API with result prefix `user`. Now in any Say or Agent prompt:

```
Hello {{user_json.customer_name}}!

Your order {{user_json.recent_orders[0].order_id}} for a
{{user_json.recent_orders[0].items[0].product_name}} is
{{user_json.recent_orders[0].status}}.

Today is {{$format($now(), 'MMMM D, YYYY')}}.
```

**Becomes at runtime:**

```
Hello John!

Your order ORD-99281 for a Classic Jacket is Delivered.

Today is April 8, 2026.
```

That's it. Everything between `{{` and `}}` is evaluated and replaced.

#### Where You Can Use Expressions

| Node Type | Where         | Example                                                                        |
| --------- | ------------- | ------------------------------------------------------------------------------ |
| **Say**   | Message text  | `Your balance is {{$format(balance_json.amount, 'currency')}}`                 |
| **Agent** | System prompt | `Customer: {{user_json.customer_name}}, Loyalty: {{user_json.loyalty_status}}` |
| **Menu**  | Prompt text   | `You have {{items.length}} options`                                            |

**Important — silent failures:** If an expression has a typo or references missing data, it silently produces empty text (no crash, no error shown to the user). Double-check your variable names! The expression `{{blance}}` won't error — it just disappears from the output.

***

### 2. Working with API Data

When an API node runs, the result is stored in session variables using the **result prefix** you set on the API node.

If you set the prefix to `user`, you get two variables:

| Variable      | What It Contains                        | When to Use                                                    |
| ------------- | --------------------------------------- | -------------------------------------------------------------- |
| `user_result` | Text formatted for the AI to read       | In Agent system prompts — `The customer data: {{user_result}}` |
| `user_json`   | The raw JSON data as a navigable object | When you need specific fields — `{{user_json.email}}`          |

`user_json` is only created when the API returns valid JSON. If the API returns plain text, only `user_result` is available.

#### Navigating JSON Fields

Use dot notation to reach into the data:

```javascript
user_json.customer_name                             // "John"
user_json.email                                     // "john@email.com"
user_json.recent_orders[0].order_id                 // "ORD-99281"
user_json.recent_orders[0].items[0].product_name    // "Classic Jacket"
user_json.recent_orders.length                      // 2
```

#### Missing Data Won't Break Your Flow

You don't need special syntax for missing data. If a field doesn't exist, it produces **empty text** in your prompt instead of crashing.

```
{{user_json.address.city}}           // Empty text if address is null — no crash
{{user_json.orders[0].items}}        // Empty text if orders is empty — no crash
```

#### Providing Default Values

*Use these when data might be missing and you want to show something instead of blank text.*

**`??` (nullish coalesce)** — replaces `null` or `undefined` with a fallback:

```
{{user_json.customer_name ?? 'Valued Customer'}}
{{user_json.address.city ?? 'City not on file'}}
{{user_json.recent_orders[0].order_id ?? 'No orders found'}}
```

**Ternary `? :`** — for conditions like "does the array have items?":

```
{{user_json.recent_orders.length > 0 ? user_json.recent_orders[0].product_name : 'No recent orders'}}
{{user_json.phone ? user_json.phone : 'No phone on file'}}
```

**When to use which:**

| Pattern     | Use When                      | Example                                         |
| ----------- | ----------------------------- | ----------------------------------------------- |
| `??`        | Field might be null/undefined | `{{name ?? 'Guest'}}`                           |
| `? :`       | Need to check a condition     | `{{items.length > 0 ? items[0].name : 'none'}}` |
| *(nothing)* | Empty text is fine            | `{{user_json.nickname}}`                        |

**Gotcha: `??` does NOT catch empty string `""` or zero `0`.** If the API returns `""`, `{{name ?? 'Guest'}}` still shows blank. Use `||` instead: `{{name || 'Guest'}}` catches null, undefined, empty string, 0, and false.

#### What Values Look Like in Prompts

| Value in data       | Shows in prompt as        | Notes                        |
| ------------------- | ------------------------- | ---------------------------- |
| `null`              | *(empty — nothing shown)* |                              |
| `undefined`         | *(empty — nothing shown)* |                              |
| `""` (empty string) | *(empty — nothing shown)* | `??` does NOT catch this!    |
| `"John"`            | `John`                    |                              |
| `42`                | `42`                      |                              |
| `0`                 | `0`                       | Shows zero, not empty        |
| `true`              | `true`                    |                              |
| `false`             | `false`                   | Shows "false", not empty     |
| `[1, 2, 3]`         | `[1,2,3]`                 | Arrays are JSON-stringified  |
| `{name: "John"}`    | `{"name":"John"}`         | Objects are JSON-stringified |

#### When the API Fails

If the API node routes to the **failure** exit, the result prefix variables (`user_result`, `user_json`) are empty or undefined. Any expression referencing them will produce empty text on the failure path.

Also watch for: an API that returns HTTP 200 with an **empty results array** (e.g., `{"results": []}`) — this routes to the **success** exit, but `{{results_json.results[0].name}}` will be empty since there's no first element.

**Tip — unique prefixes:** If your flow calls multiple APIs, give each a unique prefix. Reusing the same prefix (e.g., `api` for both calls) silently overwrites the first result when the second call runs.

#### Filtering & Transforming Arrays

*Use these when you need to pull specific items from a list, count matches, or combine values.*

| What You Want           | Expression                                                    | Result                   |
| ----------------------- | ------------------------------------------------------------- | ------------------------ |
| Filter by condition     | `user_json.orders.filter(x => x.status === 'Delivered')`      | Matching orders          |
| Get one field from each | `user_json.orders.map(x => x.order_id)`                       | `["ORD-99281", ...]`     |
| Find one item           | `user_json.orders.find(x => x.order_id === 'ORD-99281')`      | Single order             |
| Count matches           | `user_json.orders.filter(x => x.status === 'Pending').length` | `3`                      |
| Sum values              | `user_json.orders.reduce((sum, x) => sum + x.total, 0)`       | `299.97`                 |
| Any match?              | `user_json.orders.some(x => x.status === 'Pending')`          | `true/false`             |
| All match?              | `user_json.orders.every(x => x.status === 'Delivered')`       | `true/false`             |
| Join to text            | `user_json.orders.map(x => x.order_id).join(', ')`            | `"ORD-99281, ORD-99282"` |

#### Displaying API Data in Prompts

```
## Customer Info
Name: {{user_json.customer_name}}
Email: {{user_json.email}}
Loyalty: {{user_json.loyalty_status}}

## Current Order
Order ID: {{user_json.recent_orders[0].order_id}}
Product: {{user_json.recent_orders[0].items[0].product_name}}
Size: {{user_json.recent_orders[0].items[0].size}}
Color: {{user_json.recent_orders[0].items[0].color}}
Price: {{$format(user_json.recent_orders[0].items[0].price, 'currency')}}
```

***

### 3. Formatting Values

*Use `$format` when you need to display numbers as currency, percentages, or dates in human-readable form.*

#### Number Formatting

| Expression                        | Output      |
| --------------------------------- | ----------- |
| `{{$format(1234.5, 'currency')}}` | `$1,234.50` |
| `{{$format(0.15, 'percent')}}`    | `15%`       |
| `{{$format(1234567, 'number')}}`  | `1,234,567` |

#### Date Formatting

| Expression                                              | Output                 |
| ------------------------------------------------------- | ---------------------- |
| `{{$format($now(), 'date')}}`                           | `4/8/2026`             |
| `{{$format($now(), 'time')}}`                           | `3:30:00 PM`           |
| `{{$format($now(), 'datetime')}}`                       | `4/8/2026, 3:30:00 PM` |
| `{{$format($now(), 'MMMM D, YYYY')}}`                   | `April 8, 2026`        |
| `{{$format($now(), 'MM/DD/YYYY')}}`                     | `04/08/2026`           |
| `{{$format(order_json.delivery_date, 'MMMM D, YYYY')}}` | `January 14, 2026`     |

**Date format tokens:** `MMMM` (January), `MMM` (Jan), `MM` (01), `D` (8), `DD` (08), `YYYY` (2026)

***

### 4. Operators & Syntax

*Use these when building conditions or computing values inside `{{expressions}}`.*

#### Comparison

| Operator          | Meaning           | Example                  |
| ----------------- | ----------------- | ------------------------ |
| `===`             | Equals (strict)   | `status === 'active'`    |
| `!==`             | Not equals        | `status !== 'cancelled'` |
| `>` `>=` `<` `<=` | Greater/less than | `balance > 1000`         |

#### Logical

| Operator | Meaning                    | Example                                    |
| -------- | -------------------------- | ------------------------------------------ |
| `&&`     | AND                        | `balance > 0 && status === 'active'`       |
| `\|\|`   | OR                         | `tier === 'gold' \|\| tier === 'platinum'` |
| `!`      | NOT                        | `!user_json.is_blocked`                    |
| `??`     | Default for null/undefined | `name ?? 'Guest'`                          |

#### Arithmetic

| Operator        | Example            |
| --------------- | ------------------ |
| `+` `-` `*` `/` | `price * quantity` |
| `%` (remainder) | `index % 2 === 0`  |
| `**` (power)    | `2 ** 8`           |

#### Inline If (Ternary)

```javascript
{{balance > 0 ? 'positive' : 'overdrawn'}}
{{items.length > 0 ? items.length + ' items' : 'empty cart'}}
```

#### What's NOT Supported

Expressions are **read-only** — you can look up and compute values, but not change them.

```javascript
x = 5                 // REJECTED — expressions can't assign values
if (x) { ... }        // REJECTED — use ternary: x ? a : b
for (...)             // REJECTED — use .filter() / .map() instead
let x = 5            // REJECTED — no variable declarations
x => { return x }    // REJECTED — use concise form: x => x
obj?.property         // REJECTED — not needed (safe navigation is built in)
arr.push(item)        // REJECTED — use [...arr, item]
arr.sort()            // REJECTED — use arr.toSorted()
new Date()            // REJECTED — use $now() instead
```

***

### 5. String Methods

*Use these when you need to clean up, search, or transform text from API results.*

| Method                     | What It Does      | Example                                             |
| -------------------------- | ----------------- | --------------------------------------------------- |
| `.toUpperCase()`           | ALL CAPS          | `name.toUpperCase()` → `"JOHN"`                     |
| `.toLowerCase()`           | all lowercase     | `name.toLowerCase()` → `"john"`                     |
| `.trim()`                  | Remove whitespace | `input.trim()`                                      |
| `.includes('text')`        | Contains?         | `name.includes('John')` → `true`                    |
| `.startsWith('text')`      | Starts with?      | `name.startsWith('J')` → `true`                     |
| `.endsWith('text')`        | Ends with?        | `name.endsWith('n')` → `true`                       |
| `.split(',')`              | Split to array    | `"a,b,c".split(',')` → `["a","b","c"]`              |
| `.replace('old','new')`    | Replace first     | `"hello".replace('l','r')` → `"herlo"`              |
| `.replaceAll('old','new')` | Replace all       | `"hello".replaceAll('l','r')` → `"herro"`           |
| `.slice(0, 5)`             | Substring         | `"hello world".slice(0,5)` → `"hello"`              |
| `.padStart(10, '0')`       | Pad left          | `"42".padStart(5,'0')` → `"00042"`                  |
| `.length`                  | Character count   | `name.length` → `4` (**property, no parentheses!**) |

***

### 6. Array Methods

*Use these when you need to search, filter, count, or transform lists from API results.*

| Method                     | What It Does   | Example                                        |
| -------------------------- | -------------- | ---------------------------------------------- |
| `[0]`                      | First item     | `items[0]`                                     |
| `.at(-1)`                  | Last item      | `items.at(-1)`                                 |
| `.length`                  | Count          | `items.length` (**property, no parentheses!**) |
| `.filter(x => ...)`        | Keep matching  | `items.filter(x => x.active)`                  |
| `.map(x => ...)`           | Transform each | `items.map(x => x.name)`                       |
| `.find(x => ...)`          | First match    | `items.find(x => x.id === 5)`                  |
| `.some(x => ...)`          | Any match?     | `items.some(x => x.valid)` → `true/false`      |
| `.every(x => ...)`         | All match?     | `items.every(x => x.valid)` → `true/false`     |
| `.includes(val)`           | Contains?      | `items.includes('apple')` → `true/false`       |
| `.join(', ')`              | Join to text   | `items.join(', ')` → `"a, b, c"`               |
| `.slice(0, 5)`             | First N items  | `items.slice(0, 5)`                            |
| `.reduce((a,x) => ..., 0)` | Aggregate      | `items.reduce((sum, x) => sum + x.price, 0)`   |
| `.toSorted()`              | Sort (safe)    | `items.toSorted()`                             |
| `.toReversed()`            | Reverse (safe) | `items.toReversed()`                           |
| `[...new Set(items)]`      | Unique values  | Remove duplicates                              |

**Note:** `.sort()`, `.reverse()`, `.push()`, `.pop()` are blocked because they modify the original data. Use `.toSorted()`, `.toReversed()`, and spread `[...arr, newItem]` instead.

***

### 7. Built-In Globals

*Use these for math, type conversion, and encoding.*

#### Math

| Expression          | Result         |
| ------------------- | -------------- |
| `Math.round(4.5)`   | `5`            |
| `Math.floor(4.8)`   | `4`            |
| `Math.ceil(4.2)`    | `5`            |
| `Math.abs(-5)`      | `5`            |
| `Math.min(1, 2, 3)` | `1`            |
| `Math.max(1, 2, 3)` | `3`            |
| `Math.random()`     | `0.0` to `1.0` |

#### Type Conversion

| Expression           | Result                       |
| -------------------- | ---------------------------- |
| `String(123)`        | `"123"`                      |
| `Number("42")`       | `42`                         |
| `Boolean(1)`         | `true`                       |
| `parseInt("42px")`   | `42`                         |
| `parseFloat("3.14")` | `3.14`                       |
| `typeof value`       | `"string"`, `"number"`, etc. |

#### JSON & Object

| Expression              | Result                           |
| ----------------------- | -------------------------------- |
| `JSON.parse('{"a":1}')` | `{ a: 1 }`                       |
| `JSON.stringify(obj)`   | `'{"a":1}'`                      |
| `Object.keys(user)`     | `["name", "email", "age"]`       |
| `Object.values(user)`   | `["John", "john@email.com", 30]` |

#### Encoding

| Expression                    | Result        |
| ----------------------------- | ------------- |
| `btoa('hello')`               | Base64 encode |
| `atob('aGVsbG8=')`            | Base64 decode |
| `encodeURIComponent('a=b')`   | URL-encode    |
| `decodeURIComponent('a%3Db')` | URL-decode    |

***

### 8. $ Helper Functions

*These are built-in utility functions (34 total) for dates, formatting, and data access. They all start with `$`.*

#### Quick Index

`$addDays` `$addHours` `$addMinutes` `$addMonths` `$addSeconds` `$addYears` `$bigint` `$dateDiff` `$day` `$dayName` `$dayOfWeek` `$escapeHtml` `$format` `$fromToon` `$get` `$groupBy` `$guid` `$hash` `$hour` `$isAfter` `$isBefore` `$isBetween` `$isBigInt` `$isEmpty` `$isValidDate` `$minute` `$month` `$now` `$parseDate` `$sortBy` `$timeAgo` `$today` `$toToon` `$uuid` `$year`

#### Date & Time

*Use these when you need to show dates, calculate deadlines, or check time-based conditions.*

| Function                 | Returns          | Example                                            |
| ------------------------ | ---------------- | -------------------------------------------------- |
| `$now()`                 | Current datetime | `"2026-04-08T15:30:00.000Z"`                       |
| `$today()`               | Current date     | `"2026-04-08"`                                     |
| `$addDays(date, n)`      | Date + n days    | `$addDays('2026-01-15', 7)` → `"2026-01-22T..."`   |
| `$addHours(date, n)`     | Date + n hours   | `$addHours($now(), 2)`                             |
| `$addMinutes(date, n)`   | Date + n minutes | `$addMinutes($now(), 30)`                          |
| `$addSeconds(date, n)`   | Date + n seconds | `$addSeconds($now(), 90)`                          |
| `$addMonths(date, n)`    | Date + n months  | `$addMonths('2026-01-15', 3)`                      |
| `$addYears(date, n)`     | Date + n years   | `$addYears($now(), 1)`                             |
| `$dateDiff(a, b, unit?)` | Difference       | `$dateDiff('2026-01-10', '2026-01-15')` → `5` days |

#### Date Parts

*Use these to extract components from a date — useful for time-based logic in prompts.*

| Function           | Returns       | Example                          |
| ------------------ | ------------- | -------------------------------- |
| `$year(date)`      | Year          | `$year($now())` → `2026`         |
| `$month(date)`     | Month 1-12    | `$month($now())` → `4`           |
| `$day(date)`       | Day 1-31      | `$day($now())` → `8`             |
| `$dayOfWeek(date)` | 0=Sun...6=Sat | `$dayOfWeek($now())` → `2`       |
| `$dayName(date)`   | Day name      | `$dayName($now())` → `"Tuesday"` |
| `$hour(date)`      | Hour 0-23     | `$hour($now())` → `15`           |
| `$minute(date)`    | Minute 0-59   | `$minute($now())` → `30`         |

#### Date Comparison & Validation

*Use these to check if dates are before, after, or within a range.*

| Function                       | Returns            | Example                                            |
| ------------------------------ | ------------------ | -------------------------------------------------- |
| `$isBefore(a, b)`              | boolean            | `$isBefore('2026-01-01', '2026-06-01')` → `true`   |
| `$isAfter(a, b)`               | boolean            | `$isAfter('2026-06-01', '2026-01-01')` → `true`    |
| `$isBetween(date, start, end)` | boolean            | `$isBetween($today(), '2026-01-01', '2026-12-31')` |
| `$isValidDate(str)`            | boolean            | `$isValidDate('2026-02-30')` → `false`             |
| `$parseDate(str)`              | ISO string or null | `$parseDate('January 15, 2026')`                   |
| `$timeAgo(date)`               | Relative text      | `$timeAgo('2026-04-06')` → `"2 days ago"`          |

#### Data Access & Transformation

*Use these to safely reach into nested data, sort lists, or check for empty values.*

| Function                   | Returns                  | Example                                            |
| -------------------------- | ------------------------ | -------------------------------------------------- |
| `$get(obj, path, default)` | Deep value with fallback | `$get(user, 'address.city', 'Unknown')`            |
| `$groupBy(arr, key)`       | Grouped object           | `$groupBy(orders, 'status')`                       |
| `$sortBy(arr, key, dir?)`  | Sorted array             | `$sortBy(users, 'lastName')`                       |
| `$isEmpty(val)`            | boolean                  | `$isEmpty(null)` → `true`, `$isEmpty([])` → `true` |

#### Utility

*Use these for generating IDs, hashing data, or optimizing data for AI prompts.*

| Function              | Returns              | Example                       |
| --------------------- | -------------------- | ----------------------------- |
| `$guid()` / `$uuid()` | UUID string          | `"a1b2c3d4-e5f6-..."`         |
| `$hash(str, algo?)`   | Hash string          | `$hash('data', 'sha256')`     |
| `$escapeHtml(str)`    | Escaped HTML         | `$escapeHtml('<b>hi</b>')`    |
| `$toToon(arr)`        | Compact table format | For AI-optimized data display |
| `$fromToon(str)`      | Parsed array         | Parse TOON back to objects    |

***

### 9. Common Errors & What They Mean

| Error                                    | What Happened                         | How to Fix                                                                                          |
| ---------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------- |
| **Unknown variable(s): blance**          | Typo in variable name                 | Check spelling — did you mean `balance`?                                                            |
| **Unknown method 'toUpper' on string**   | Wrong method name                     | Use `toUpperCase()` instead                                                                         |
| **'length' is a property, not a method** | Called `.length()` with parentheses   | Remove the `()` — use `.length`                                                                     |
| **Unexpected end of expression**         | Missing closing `)`, `]`, or `'`      | Check for unmatched brackets or quotes                                                              |
| **Timeout (2000ms)**                     | Expression took too long              | Simplify — avoid huge arrays or deep chains                                                         |
| **Maximum array length exceeded**        | Array > 10,000 items                  | Filter or slice first: `.slice(0, 100)`                                                             |
| **Maximum string length exceeded**       | String > 100,000 chars                | Truncate: `.slice(0, 1000)`                                                                         |
| **Security: blocked property access**    | Accessed `__proto__` or `constructor` | These are blocked for security — use a different approach                                           |
| **Block-body arrow not supported**       | Used `x => { return ... }`            | Use concise form: `x => x.active`                                                                   |
| *(Silent empty text)*                    | Expression failed or data is missing  | Check variable names and API prefix — failed expressions produce blank output with no visible error |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ixhello.com/ixhc2/technical-specifications/expression-engine/library-reference.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
