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

# Integration Mapping Guide

> How integration mapping rules, field paths, templates, transformations, and fallbacks work — and what is not supported

> For the complete documentation index, see [llms.txt](/llms.txt)

Integration mappings define how Luminous data is reshaped before it is pushed to (or
pulled from) an external system — Shopify, Trackstar, an EDI partner, and so on. A
mapping is a list of **rules**; each rule reads a value from the source payload,
optionally transforms it, and writes it to a field in the destination payload.

This page documents the mapping **engine** — the field-path syntax, template syntax,
the full catalogue of transformations, and the fallback mechanics. For the CRUD
endpoints that store mappings, see the [Integration Mappings](/api-reference) section
of the API Reference.

## Two mapping formats

The engine accepts two rule formats and auto-detects which one a mapping uses by
inspecting the first rule:

| Format                        | Rule keys                       | Capabilities                                                                          |
| ----------------------------- | ------------------------------- | ------------------------------------------------------------------------------------- |
| **FieldMapper** (recommended) | `lumField`, `pushField`         | Full feature set — templates, array wildcards, every transformation                   |
| **Legacy**                    | `sourcePath`, `destinationPath` | Dot-notation paths only — no templates, no wildcards. Adds `destField` type coercion. |

A mapping is treated as FieldMapper format when its first rule has **both** `lumField`
and `pushField`. Otherwise it falls back to legacy processing. New mappings should use
the FieldMapper format; the rest of this guide describes it.

## Rule structure

Each rule is an object with these keys:

| Key                    | Required | Description                                                                                                               |
| ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `pushField`            | Yes      | Destination path the value is written to.                                                                                 |
| `lumField`             | No       | Source path the value is read from. May be a [template expression](#template-expressions). Omit when using `staticValue`. |
| `staticValue`          | No       | A fixed value. When set, it is used directly and `lumField` is ignored.                                                   |
| `defaultValue`         | No       | Used when the resolved value is empty and no `staticValue` was given.                                                     |
| `transformation`       | No       | Name of a single [transformation](#transformations) to apply.                                                             |
| `transformationConfig` | No       | Configuration object for that transformation.                                                                             |
| `id`                   | No       | Optional identifier, surfaced in logs when a rule fails.                                                                  |

Resolution order for a single rule:

1. If `staticValue` is set, use it.
2. Otherwise, if `lumField` is a template, evaluate the template; if it is a plain
   path, extract that path from the source.
3. If the result is *empty* (`null`, `""`, or `[]`) and `defaultValue` is set, use `defaultValue`.
4. If `transformation` is set **and** the value is not empty, apply the transformation.
5. Write the value to `pushField`.

<Note>
  Each rule applies **at most one** transformation. There is no transformation chain —
  to apply several steps, use a [`customFunction`](#customfunction) or a template.
</Note>

## Field paths

Both `lumField` and `pushField` support the same path syntax:

* **Dot notation** — `shipToAddress.name`, `lumSku.customFields.brand`
* **Array wildcard** — `items[*].sku` extracts `sku` from every element of `items`.
  As a destination, it writes element-wise into the destination array.
* **Array index** — `items[0].sku` targets a single element.

### Scalar broadcast

When a scalar value is written to a wildcard destination (for example a single
`retailPrice` mapped to `variants[*].price`), the value is **broadcast** into every
slot of the destination array. Because of this, rules that populate the array with
real per-element data (e.g. `variants[*].sku`) should run **before** scalar broadcasts
so the slot count is correct.

## Template expressions

If `lumField` contains `{{ }}`, it is evaluated as a template instead of a plain path.

```text theme={null}
Order {{orderNumber}} for {{shipToAddress.name}}
```

Supported template features:

* **Interpolation** — `{{field}}`, including dotted and indexed paths (`{{items[0].sku}}`).
* **Fallback chains** — `{{customerPoNumber|orderNumber|'NO-REF'}}` tries each field in
  turn and returns the first non-empty one; a quoted segment is a literal default.
* **`concat` helper** — `{{concat shipToAddress.street1 ', ' shipToAddress.city}}`.

<Warning>
  `concat` is the **only** helper. Any other helper name is returned verbatim, unevaluated.
  Templates have no conditionals, loops, or arithmetic — use a `customFunction` for those.
</Warning>

## Transformations

A transformation is applied via the `transformation` key (the name) and
`transformationConfig` (its options). The complete supported set:

### String

| Name        | Config                   | Notes                                                             |
| ----------- | ------------------------ | ----------------------------------------------------------------- |
| `uppercase` | —                        | Non-strings pass through unchanged.                               |
| `lowercase` | —                        | Non-strings pass through unchanged.                               |
| `trim`      | —                        | Trims leading/trailing whitespace.                                |
| `split`     | `delimiter`, `partIndex` | Splits and returns one part (default delimiter `" "`, index `0`). |
| `replace`   | `find`, `replace`        | Plain string find/replace (not regex).                            |
| `substring` | `start`, `length`        | `length` optional.                                                |
| `left`      | `length`                 | Leftmost N characters.                                            |
| `right`     | `length`                 | Rightmost N characters.                                           |
| `padLeft`   | `length`, `char`         | Pads to length (default char is a space).                         |
| `padRight`  | `length`, `char`         | Pads to length.                                                   |
| `truncate`  | `length`, `ellipsis`     | Truncates long strings, optional ellipsis suffix.                 |
| `length`    | —                        | Returns string length, or element count for an array.             |

### Number

| Name    | Config | Notes                      |
| ------- | ------ | -------------------------- |
| `round` | —      | Rounds to nearest integer. |
| `abs`   | —      | Absolute value.            |

### Date

All date transforms parse the input with Carbon and return a `Y-m-d` string. If the
input cannot be parsed, the original value is returned unchanged.

| Name           | Config   | Notes                           |
| -------------- | -------- | ------------------------------- |
| `addDays`      | `amount` | Negative values subtract.       |
| `addMonths`    | `amount` | Negative values subtract.       |
| `startOfMonth` | —        | First day of the value's month. |
| `endOfMonth`   | —        | Last day of the value's month.  |

### Type coercion

| Name        | Config | Notes                                                                |
| ----------- | ------ | -------------------------------------------------------------------- |
| `toString`  | —      | `null` → `""`, booleans → `"true"`/`"false"`, arrays/objects → JSON. |
| `toNumber`  | —      | Parses numbers out of strings; non-numeric → `0`.                    |
| `toBoolean` | —      | Recognises `true/yes/1/on/enabled` and their negatives.              |

### Lookup and extraction

| Name             | Config                 | Notes                                                      |
| ---------------- | ---------------------- | ---------------------------------------------------------- |
| `valueMapping`   | `mappings`, `fallback` | Lookup table — see below.                                  |
| `jsonExtract`    | see below              | Extracts values out of JSON strings/arrays.                |
| `customFunction` | `code`                 | Runs custom logic — see [customFunction](#customfunction). |

#### valueMapping

Looks the value up in a `mappings` table. Key matching is **case-insensitive**. If no
key matches, the `fallback` is used; if no `fallback` is configured, the original
value passes through unchanged.

```json theme={null}
{
  "transformation": "valueMapping",
  "transformationConfig": {
    "mappings": { "United States": "US", "Canada": "CA" },
    "fallback": "XX"
  }
}
```

#### jsonExtract

Parses a JSON string (or accepts an array/object directly) and pulls values out of it.

| Config key      | Description                                                                     |
| --------------- | ------------------------------------------------------------------------------- |
| `path`          | Selector. Only `$[*]` (all elements) and `$[*].fieldName` are supported.        |
| `extract`       | Shortcut — a field name to pull from each element instead of `path`.            |
| `joinWith`      | Joins extracted scalars into a single delimited string.                         |
| `skipEmpty`     | Drop empty values (default `true`).                                             |
| `dedupe`        | Remove duplicates (default `false`).                                            |
| `projectFields` | Reshape each element — map of `outputKey → sourceKey`.                          |
| `staticFields`  | Fixed key/values merged into every projected element.                           |
| `requireAny`    | `{ "field": ..., "contains": ... }` — yields nothing unless an element matches. |
| `onInvalidJson` | `returnOriginal` (default), `fallback`, or `null`.                              |
| `fallback`      | Value used when extraction is empty or JSON is invalid.                         |

<Warning>
  `jsonExtract` does **not** implement full JSONPath. Only the two path shapes above
  work; any other selector returns nothing.
</Warning>

## Defaults and fallbacks

Fallback behaviour exists at several layers — they stack:

1. **Template fallback chains** — `{{a|b|'literal'}}` resolves to the first non-empty field.
2. **Rule `defaultValue`** — used when the resolved value is empty before transformation.
3. **`valueMapping` `fallback`** — used when no lookup key matches.
4. **`jsonExtract` `fallback` / `onInvalidJson`** — used when extraction yields nothing or the JSON is invalid.
5. **Engine error handling** — see below.

## Error handling

The engine runs in **non-strict mode** for integration pushes: if a single rule throws,
the error is logged and the engine continues with the remaining rules. A broken rule
drops its field rather than failing the whole push.

After all rules run, the result is cleaned: `null` values and empty strings are
**stripped** from the output payload. A rule that resolves to empty therefore
produces no destination key at all.

## customFunction

`customFunction` runs caller-supplied JavaScript. The JavaScript is converted to PHP by
an LLM, security-scanned, cached, and executed.

```json theme={null}
{
  "transformation": "customFunction",
  "transformationConfig": {
    "code": "if (typeof value === 'number') { return '$' + value.toFixed(2); } return value;"
  }
}
```

**Requirements and behaviour:**

* Conversion requires `OPENAI_API_KEY` to be configured on the environment. Without it,
  only a handful of built-in patterns are recognised (price formatting, SKU padding,
  simple prefix/suffix concatenation, type-checked multiplication) — anything else fails.
* Converted code is cached for one hour, keyed by a hash of the JavaScript, so repeated
  pushes do not re-call the LLM.
* The converted PHP is **security-scanned** before it runs. File system, network,
  process execution, database, reflection, eval, and data-exfiltration calls are
  blocked; the code must define a single `customTransform` function. Code that exceeds
  the risk threshold is rejected and the push fails for that rule.

<Note>
  Reach for `customFunction` only when the built-in transformations and templates cannot
  express what you need. It is the most powerful option but also the slowest (LLM
  conversion on a cache miss) and the most likely to be rejected by the security scan.
</Note>

## What is not supported

* **Chained transformations.** Each rule applies one transformation. There is no
  `transformations: []` array — older examples that show one are out of date.
* **`template`, `prefix`, `suffix`, `dateFormat`, `arrayMap` transformations.** These
  names appear in older material but are not implemented. Use a template expression
  for interpolation/prefix/suffix, `valueMapping` for lookups, and the `addDays` /
  `startOfMonth` family for dates.
* **Template helpers beyond `concat`.** No `if`, no loops, no math in templates.
* **Full JSONPath.** `jsonExtract` only understands `$[*]` and `$[*].fieldName`.
* **Regex find/replace.** `replace` is a literal string replacement.
