Appearance
Mapping language reference
For developers & integrators
A mapping definition is a JSON (or YAML) object. The engine walks it and returns a document with the same shape. Merchant introduction: Mappings.
Node types
Four keys are reserved and select a node type. Every other key is an output field.
| Node | Syntax | Output |
|---|---|---|
| Path | {"path": "order.billingAddress.city"} | The value at a dotted path. Missing → null (never an error). Objects and lists are copied as-is. |
| Expression | {"expr": "<expression>"} | Result of a sandboxed expression. A throwing expression records a field error and yields null. |
| Literal | {"literal": <any JSON>} | The value unchanged — use it to output an object literally. |
| Bare scalar | "TEXT", 42, true, null | The value unchanged. |
| Collection | {"each": "<path>", "as": "item", "where": "<expression>", "map": <node>} | A list. each must resolve to a list (anything else → []). as defaults to item. where is optional. map is any node, evaluated with the element bound to the alias. |
| Object | {"field": <node>, …} | An object with the same keys. |
Collections nest: a map may contain another each (for example deliveries → positions). The outer alias stays in scope.
Root variables
Only order is defined. It is the full loaded order, serialised from Shopware's OrderEntity:
- field names are the entity's property names (
orderNumber,amountTotal,orderDateTime,customerComment…); - entities become objects, collections become lists (so
eachcan iterate them); - dates become ISO-8601 strings (
2026-09-01T10:15:00+00:00), backed enums their value; extensionsare dropped;- only loaded associations are present.
Loaded associations: stateMachineState, currency, language, salesChannel, orderCustomer, lineItems, billingAddress.country, addresses.country, deliveries (with shippingOrderAddress.country, shippingMethod, stateMachineState), transactions (with paymentMethod, stateMachineState), documents.documentType, tags, primaryOrderDelivery, primaryOrderTransaction. Not loaded by default: lineItems.product, document media, transaction captures, createdBy/updatedBy. Developers can add associations with the criteria event.
Useful paths:
| Path | Example value |
|---|---|
order.orderNumber | "10001" |
order.orderDateTime | "2026-09-01T10:15:00+00:00" |
order.amountTotal / order.amountNet | 119.0 / 100.0 |
order.currency.isoCode | "NGN" |
order.orderCustomer.email | "ada@example.com" |
order.billingAddress.country.iso | "NG" |
order.stateMachineState.technicalName | "open" |
order.lineItems | list of line items |
item.type | "product", "promotion", "credit", "custom" |
item.payload.productNumber | "SW10001" |
item.quantity, item.unitPrice, item.totalPrice | 2, 50.0, 100.0 |
item.price.calculatedTaxes | list of {taxRate, tax, price} |
Render a real order with Test mapping (or the preview endpoint) to see every field.
Expressions
Expressions use Symfony ExpressionLanguage syntax, sandboxed: the built-in constant() is removed and only the functions below exist.
| Kind | Examples |
|---|---|
| Property access | order.billingAddress.city, item.price.calculatedTaxes[0].taxRate |
| Arithmetic | item.unitPrice * item.quantity, order.amountTotal - order.amountNet |
| Comparison / logic | item.type == 'product' and item.quantity > 0, not item.good |
| Ternary | order.amountTotal > 100 ? 'priority' : 'standard' |
| Membership | order.currency.isoCode in ['NGN', 'GHS'] |
| String concatenation | order.orderNumber ~ '-' ~ order.salesChannel.name |
Functions
| Function | Signature | Behaviour |
|---|---|---|
concat | concat(...parts) | Joins parts as strings with no separator; null parts are skipped. |
default | default(value, fallback) | fallback if value is null or ''. |
money | money(amount, decimals = 2) | Rounds half-up; returns a float. Non-numeric amount is an error. |
format_date | format_date(value, format = 'Y-m-d', timezone = 'UTC') | Accepts a date string or Unix timestamp; '' for empty input. Format characters. |
country_iso | country_iso(value) | Trims and upper-cases; '' for empty input; non-string is an error. |
tax_rate | tax_rate(value) | A numeric value → float. A list of {taxRate} rows is meant to return the highest rate, but returns 0.0 in 0.9.0-beta.1 because list rows reach the function as objects. Use tax_rate(item.price.calculatedTaxes[0].taxRate). |
Developers can add functions — see Extension points.
Validation
On save (and via the validate endpoint) every reserved value must be a string, and every expression is compiled against the variables in scope (order, plus the aliases of enclosing collections). Unknown variables, unknown functions and syntax errors are reported as <field path>: <problem>, e.g. total: "expr" must be a string.
At render time, field errors are collected per path (lines[0].vat) instead of stopping at the first. An export whose render has any field error is not sent — it fails permanently and is not retried.
Output formats
| Target format | Serialiser | Content-Type |
|---|---|---|
json | JSON | application/json |
xml | Symfony XML encoder, root element <order> | application/xml |
Determinism and de-duplication
The rendered document is hashed (SHA-256 of canonical JSON). If the hash equals the last settled export for the same order and target, the export is skipped as deduplicated. Keep mappings deterministic — no timestamps or random values — or every run will look like a change.