Portal Community

The Standard Node Data Envelope

Every node's input and output data — DataMapping included — travels around the workflow inside the same standard envelope. The envelope carries run metadata (timestamp, resource, operation, status, portName, success) plus the actual payload records under items, where each entry wraps its record in a "json" property:

{
  "timestamp": "2026-07-24T01:02:27.8652562Z",
  "triggerTime": "2026-07-24T01:02:27.8652562Z",
  "resource": "trigger",
  "operation": "manual-trigger",
  "status": "success",
  "portName": "main",
  "items": [
    {
      "json": {
        "timestamp": "2026-07-24T01:02:27.8652562Z",
        "triggerTime": "2026-07-24T01:02:27.8652562Z",
        "resource": "trigger",
        "operation": "manual-trigger",
        "status": "success",
        "portName": "main"
      }
    }
  ],
  "success": true
}

DataMapping reads its real records from InputData["items"], unwrapping each entry's "json" value — this is exactly what the shared ResolveSourceRecords/ExtractRecordsFromItems base-class helpers do. It never looks at the outer envelope fields (timestamp/resource/operation/etc.) as data — those are metadata about the upstream run, not part of the record being mapped.

Inline test data does NOT need the envelope shape

When you type JSON directly into inlineDataSource to test a mapping without wiring up an upstream node, you write plain data — a single object or a plain array of objects, whichever your mapping rules expect. You do not pre-wrap it as {"items":[{"json":{...}}]}. The node parses whatever you typed and normalizes it into the standard items/json shape itself before producing output. The envelope is the wire format between nodes, not the format you author by hand.

When to Use

Configuration

SettingRequiredDescription
inlineDataSource Optional An inline JSON string — a single object or an array of objects. This is a shared base-class setting (not unique to DataMapping). If non-empty, it wins outright over InputData["items"]: parsed and standardized into the items/json shape via the same helper every node uses. Configured via Form 100804 (formatted enhanced-json-editor). Form 100803, an older plain-textarea variant of the same field, is disabled — it rendered as a visible duplicate of 100804 in the designer and has been retired.
dataMappings Required* A JSON array of mapping-rule objects ({ sourceField, targetField, transform?, transformExpression? }). *Not required when dataMappingMode is "passthrough" — it may be empty in that mode. Configured via Form 11010.
dataMappingMode Optional Default "". "" applies the dataMappings rules normally, to every resolved record. "passthrough" skips all mapping rules and copies each resolved record straight to output unchanged. Configured via Form 11010 (select: Default / Pass Through).
sourceDirectivePath / sourceDataRootPath / sourceDataLeafPath Optional SmartPath fallback consulted only when inlineDataSource is empty and the plain InputData["items"] lookup is bypassed by a custom root path. In the common case you can ignore these entirely — the node already reads InputData["items"] by default.
targetDirectivePath / targetDataRootPath Optional Optional SmartPath write-back location for the mapped record(s). Rarely needed — the output already carries every mapped record under items[].json.

MappingRule fields (each entry in dataMappings)

FieldRequiredDescription
sourceField Required Path to the field in the source record. Supports dot notation (user.address.city) and array indexing (items[0].price, data.users[2].email). No negative indices for field paths.
targetField Required A flat key name in the output record. Plain dictionary key assignment only — no nested/dot-notation writing on the target side.
transform Optional One of the 22 transform strings below. If omitted, the value is copied as-is with no conversion.
transformExpression Optional* *Required when transform is "expression", otherwise ignored entirely. A JS expression evaluated via the same expression engine CollectionOperation uses. item (the full current record) and value (this rule's extracted sourceField value) are bound as variables — lets one rule combine multiple source fields, e.g. item.firstName + ' ' + item.lastName.

Supported Transforms (22)

TransformBehavior
"uppercase""london""LONDON"
"lowercase""SMITH""smith"
"trim"" hi ""hi"
"tostring"Identity string conversion of the value.
"toint""42"42. On parse failure, returns the original value unchanged (not null, not an error).
"todecimal""19.99"19.99 (invariant culture). Same graceful fallback-to-original on failure.
"toboolean"Only literal "true"/"false" (case-insensitive) — not "1"/"yes". Falls back to original on failure.
"tofloat""3.14"3.14 (double). Fallback to original on failure.
"todouble"Exact alias of tofloat — identical behavior.
"todate"Parses with RoundtripKind. Fallback to original on failure.
"todatetime"Exact alias of todate — identical behavior.
"base64encode"UTF-8 → Base64 string.
"base64decode"Base64 → UTF-8 string. Returns the original string unchanged if not valid Base64.
"jsonstringify"Serializes the original value (not its string form) to a JSON string.
"jsonparse"Parses the string as JSON. Returns the original string unchanged if invalid.
"nulltoempty"null"". Special-cased to run even when the value is null.
"emptytonull"""null; otherwise unchanged.
"defaultvalue:N/A"Returns the text after the first colon if the value is null or an empty string; otherwise passes the value through unchanged. Also runs on null input.
"concat:_suffix"Appends the literal text after the colon: "abc""abc_suffix".
"substring:0:5" / "substring:-3"start[:length]. Negative start counts from the end (Python-style). Omitted length takes to the end of the string.
"regexreplace:[^0-9]:"pattern:replacement (replacement may be empty). 200ms timeout; returns the original string unchanged on timeout or invalid pattern.
"expression"Evaluates transformExpression as JS, with item (full record) and value (this rule's sourceField value) bound as variables. Runs even when value is null, since an expression typically reads from item rather than value — e.g. combining two other fields. Falls back to the original value on any evaluation error.
Numeric/date/expression transforms never fail loudly

toint, todecimal, toboolean, tofloat/todouble, todate/todatetime, and expression never throw on bad input — a failed conversion or expression error silently returns the original, untransformed value instead. A malformed source value does not route to the error port; it just passes through unchanged, so downstream logic that assumes a numeric/date type should validate it explicitly. An unknown/misspelled transform name behaves the same way: value passed through unchanged, no error raised.

Every transform except nulltoempty, defaultvalue:, and expression returns null immediately when the input value is null — they don't run their conversion logic on null.

Multiple Records — One Node Handles the Whole Batch

DataMapping does not process a single object only. When the resolved source (inlineDataSource or upstream items) is an array of many records, DataMapping applies the same dataMappings rules to every record independently and emits one mapped record per input record — each becomes its own entry in the output items array, not one giant array wrapped in a single item. A single object source still produces exactly one output record. The output field recordCount tells you how many records were processed.

Passthrough Mode

Set dataMappingMode to "passthrough" to skip dataMappings entirely and copy each resolved record straight to output — dataMappings may be left empty in this mode. Use passthrough when you need the node purely to move data through the standard output envelope without renaming or converting any field.

The Data Assigner node (Template ID 10000108) is this same node type pre-configured for exactly this: dataMappingMode: "passthrough", dataMappings: []. It appears in the node palette under a separate "Data Assigner" label/icon for discoverability, but at runtime it is the identical data-mapping executor.

Nested Field Access

sourceField supports dot notation and array indexing, evaluated left to right. Examples of increasing depth:

sourceFieldMeaning
nameTop-level property.
user.address.cityNested object property.
items[0].priceFirst element of an array property, then its price field.
data.users[2].emailObject → array → indexed element → field.
response.data.attributes.billing.address.cityDeep nested extraction, flattened to a single targetField like billingCity.

Missing intermediate keys or out-of-bounds indices resolve to null rather than raising an error.

Output Ports

PortWhen It Fires
successMapping (or passthrough) completed. status, mappingFieldCount, recordCount, and items are populated.
errordataMappings is empty and dataMappingMode is not "passthrough", the source could not be resolved at all (neither inlineDataSource nor InputData["items"] present), or an unhandled exception occurs. Note the transform fallback behavior above — most conversion problems do not reach this port.

Output Fields

FieldTypeDescription
statusstringLiteral "success" on the success path.
mappingFieldCountintegerCount of mapping rules applied to each record. In passthrough mode this is still the rule count (typically 0) — it does not reflect any record's field count.
recordCountintegerNumber of records that were resolved and mapped. 0 is a valid, non-error result — it just means no output items are produced.
itemsarrayStandard [{ "json": <record> }, ...] shape — one entry per mapped record (or, in passthrough mode, per resolved source record, unchanged). Merged against any pre-existing items per the node's OutputItemMergeType setting (default: full replace).

Validation

The node validates on execution:

Sample Configuration

{
  "nodeType": "data-mapping",
  "settings": {
    "dataMappingMode": "",
    "dataMappings": [
      { "sourceField": "contact.full_name",     "targetField": "customerName",  "transform": "trim" },
      { "sourceField": "contact.email_address", "targetField": "email",         "transform": "lowercase" },
      { "sourceField": "contact.address.city",  "targetField": "city",          "transform": "uppercase" },
      { "sourceField": "contact.created_at",    "targetField": "createdDate",   "transform": "todate" },
      { "sourceField": "contact.annual_spend",  "targetField": "annualSpend",   "transform": "todecimal" },
      { "sourceField": "contact.is_active",     "targetField": "isActive",      "transform": "toboolean" },
      { "sourceField": "contact.first_name",    "targetField": "greeting",      "transform": "expression", "transformExpression": "'Hi ' + item.contact.first_name + '!'" }
    ]
  }
}

Sample Output

Success Port

{
  "status": "success",
  "mappingFieldCount": 7,
  "recordCount": 1,
  "items": [
    {
      "json": {
        "customerName": "Michael Torres",
        "email": "[email protected]",
        "city": "SEATTLE",
        "createdDate": "2025-03-15T00:00:00Z",
        "annualSpend": 48250.75,
        "isActive": true,
        "greeting": "Hi Michael!"
      }
    }
  ]
}

Expression Reference

ExpressionValue
{{ $output.dataMappingNode.items[0].json.customerName }}The trimmed customer name from the first mapped record.
{{ $output.dataMappingNode.items[0].json.city }}The uppercased city value.
{{ $output.dataMappingNode.mappingFieldCount }}Number of mapping rules applied per record (7 here).
{{ $output.dataMappingNode.recordCount }}Number of records mapped (1 here — use a Loop node downstream when this is greater than one).

Node Policies & GuardRails

PolicyRationale
Prefer DataMapping over CodeExecute for field renaming and type conversionDataMapping is declarative, auditable, and readable by non-developers. CodeExecute is appropriate for logic that cannot be expressed as per-field transforms.
Always handle the error portIt fires when no mapping rules are configured (outside passthrough mode), the source can't be resolved at all, or an unhandled exception occurs — but not for type-conversion or expression failures, which fall back silently (see the callout above). Connect it to a notification or dead-letter path.
Use passthrough mode (or the Data Assigner node) instead of one-to-one identity mappingsWriting a dataMappings rule per field just to copy it through is unnecessary busywork — set dataMappingMode: "passthrough" and leave dataMappings empty.
Only explicitly mapped fields appear in the output record (non-passthrough mode)Fields not listed in dataMappings are absent from each mapped record. If you need additional unmapped fields, add explicit mapping rules for each or switch to passthrough.
Use "expression" only when a rule genuinely needs more than one source fieldFor a plain single-field rename or type conversion, a named transform is clearer and cheaper to evaluate than a JS expression. Reach for expression specifically to combine fields (e.g. fullName) or apply conditional logic a named transform can't express.
Validate numeric/date/expression transform results explicitly if input quality is uncertainBecause failed conversions and expression errors pass through the original value unchanged rather than erroring, a malformed upstream value can silently reach downstream nodes in its original (wrong) type.

Pattern Examples

See Examples for elaborate, realistic worked examples — basic renaming, combining fields via expression, passthrough/Data Assigner usage, and using inlineDataSource directly.

Pattern 1 — Normalize CRM API Response

{
  "dataMappings": [
    { "sourceField": "data.contact.id",           "targetField": "customerId" },
    { "sourceField": "data.contact.name",         "targetField": "customerName", "transform": "trim" },
    { "sourceField": "data.contact.email",        "targetField": "email",        "transform": "lowercase" },
    { "sourceField": "data.billing.address.city", "targetField": "billingCity",  "transform": "uppercase" },
    { "sourceField": "data.billing.total",        "targetField": "totalAmount",  "transform": "todecimal" },
    { "sourceField": "data.order.created_at",      "targetField": "orderDate",    "transform": "todatetime" }
  ]
}

Pattern 2 — Passthrough via Data Assigner

{
  "dataMappingMode": "passthrough",
  "dataMappings": []
}
// items[].json = each resolved source record, unchanged