Transform Steps
Map, normalize, and validate data in workflows
Transform Steps
Transform steps manipulate and reshape data as it flows through your workflow. They support field mapping, data normalization, validation, and enrichment operations.
Configuration
{
"id": "transform-data",
"type": "transform",
"name": "Transform Customer Data",
"config": {
"action": "transform",
"sourceData": "{{input}}",
"targetSchema": {
"type": "object",
"properties": {
"fullName": { "type": "string" },
"email": { "type": "string" }
}
},
"mappingRules": [
{
"source": "first_name",
"target": "fullName",
"transform": "capitalize"
}
]
}
}| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique step identifier |
type | string | Yes | Must be "transform" |
name | string | Yes | Human-readable name |
config.action | string | No | Transform action (default: transform) |
config.sourceData | string | Yes | Template expression for source data |
config.targetSchema | object | No | JSON Schema for target structure |
config.mappingRules | array | No | Field mapping rules |
config.normalizations | array | No | Data normalization rules |
config.validationRules | array | No | Validation rules |
config.strict | boolean | No | Fail on any transformation error |
Transform Actions
| Action | Description |
|---|---|
transform | Full transformation with mapping and normalization |
map | Simple field-to-field mapping |
normalize | Standardize data formats |
validate | Validate data against schema |
enrich | Add computed or external data |
Transform Types
Transforms can be categorized by type:
| Type | Description |
|---|---|
mapping | Field-to-field transformations |
normalization | Data standardization |
enrichment | Adding external or computed data |
aggregation | Combining multiple values |
custom | Custom transformation logic |
Mapping Rules
Mapping rules define how fields are transformed from source to target:
{
"config": {
"action": "map",
"sourceData": "{{input}}",
"mappingRules": [
{
"source": "customer.firstName",
"target": "name.first",
"transform": "capitalize"
},
{
"source": "customer.lastName",
"target": "name.last",
"transform": "uppercase"
},
{
"source": "customer.emailAddress",
"target": "email",
"transform": "lowercase",
"required": true
}
]
}
}Mapping Rule Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | No | Unique rule identifier |
source | string | Yes | JSONPath or dot notation to source field |
target | string | Yes | JSONPath or dot notation to target field |
transform | string | No | Transformation operation (default: direct) |
customTransform | string | No | Natural language transformation description |
default | any | No | Default value if source is missing |
required | boolean | No | Whether source field is required |
condition | string | No | Condition for applying this rule |
Field Transforms
| Transform | Description | Example |
|---|---|---|
direct | Copy value as-is | "John" → "John" |
uppercase | Convert to uppercase | "John" → "JOHN" |
lowercase | Convert to lowercase | "John" → "john" |
trim | Remove whitespace | " John " → "John" |
capitalize | Capitalize first letter | "john" → "John" |
titlecase | Title Case Each Word | "john doe" → "John Doe" |
snake_case | Convert to snake_case | "firstName" → "first_name" |
camelCase | Convert to camelCase | "first_name" → "firstName" |
number | Parse as number | "123" → 123 |
string | Convert to string | 123 → "123" |
boolean | Parse as boolean | "true" → true |
array | Wrap in array | "value" → ["value"] |
flatten | Flatten nested array | [[1], [2]] → [1, 2] |
custom | Custom transformation | Use with customTransform |
Conditional Mapping
Apply mappings conditionally:
{
"mappingRules": [
{
"source": "amount",
"target": "total",
"transform": "number",
"condition": "{{input.currency}} === 'USD'"
},
{
"source": "amount_eur",
"target": "total",
"transform": "number",
"condition": "{{input.currency}} === 'EUR'"
}
]
}Default Values
Provide fallback values for missing fields:
{
"mappingRules": [
{
"source": "customer.phone",
"target": "contactPhone",
"default": "Not provided"
},
{
"source": "preferences.language",
"target": "locale",
"default": "en-US"
}
]
}Normalization Rules
Normalization rules standardize data formats:
{
"config": {
"action": "normalize",
"sourceData": "{{input}}",
"normalizations": [
{
"field": "created_date",
"type": "date",
"format": "YYYY-MM-DD"
},
{
"field": "amount",
"type": "currency",
"locale": "en-US"
},
{
"field": "phone_number",
"type": "phone",
"format": "E.164"
}
]
}
}Normalization Types
| Type | Description | Example |
|---|---|---|
date | Standardize date format | "01/15/2024" → "2024-01-15" |
currency | Format currency values | 1234.5 → "$1,234.50" |
phone | Standardize phone numbers | "(555) 123-4567" → "+15551234567" |
address | Normalize address format | Standardize street, city, zip |
name | Standardize name format | "JOHN DOE" → "John Doe" |
email | Normalize email addresses | "John@EXAMPLE.com" → "john@example.com" |
url | Standardize URLs | Add protocol, normalize case |
percentage | Format percentages | 0.156 → "15.6%" |
Normalization Rule Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | No | Unique rule identifier |
field | string | Yes | Field path to normalize |
type | string | Yes | Type of normalization |
format | string | No | Target format (e.g., YYYY-MM-DD for dates) |
locale | string | No | Locale for formatting (e.g., en-US) |
options | object | No | Additional normalization options |
Date Normalization Examples
{
"normalizations": [
{
"field": "order_date",
"type": "date",
"format": "YYYY-MM-DD"
},
{
"field": "timestamp",
"type": "date",
"format": "ISO8601"
},
{
"field": "birth_date",
"type": "date",
"format": "MM/DD/YYYY",
"locale": "en-US"
}
]
}Currency Normalization Examples
{
"normalizations": [
{
"field": "price",
"type": "currency",
"locale": "en-US",
"options": {
"currency": "USD",
"minimumFractionDigits": 2
}
},
{
"field": "total_eur",
"type": "currency",
"locale": "de-DE",
"options": {
"currency": "EUR"
}
}
]
}Validation Rules
Validate transformed data against constraints:
{
"config": {
"action": "validate",
"sourceData": "{{steps.transform.output}}",
"validationRules": [
{
"field": "email",
"rule": "required",
"message": "Email is required"
},
{
"field": "email",
"rule": "pattern",
"value": "^[^@]+@[^@]+\\.[^@]+$",
"message": "Invalid email format"
},
{
"field": "age",
"rule": "range",
"value": { "min": 18, "max": 120 },
"message": "Age must be between 18 and 120"
}
]
}
}Validation Rule Types
| Rule | Description | Value Example |
|---|---|---|
required | Field must exist and not be null | N/A |
type | Field must be specific type | "string", "number", "boolean" |
pattern | Field must match regex | "^[A-Z]{2}$" |
range | Numeric range | { "min": 0, "max": 100 } |
enum | Must be one of listed values | ["active", "inactive", "pending"] |
custom | Custom validation expression | "{{value}} > 0" |
Validation Rule Fields
| Field | Type | Required | Description |
|---|---|---|---|
id | string | No | Unique rule identifier |
field | string | Yes | Field path to validate |
rule | string | Yes | Validation type |
value | any | Depends | Validation parameter |
message | string | No | Custom error message |
Target Schema
Define the expected output structure:
{
"config": {
"action": "transform",
"sourceData": "{{input}}",
"targetSchema": {
"type": "object",
"required": ["customerId", "email"],
"properties": {
"customerId": {
"type": "string",
"pattern": "^cus_[a-zA-Z0-9]+$"
},
"email": {
"type": "string",
"format": "email"
},
"name": {
"type": "object",
"properties": {
"first": { "type": "string" },
"last": { "type": "string" }
}
},
"tags": {
"type": "array",
"items": { "type": "string" }
}
}
},
"mappingRules": [...]
}
}Using Templates
Use pre-built transform templates:
{
"config": {
"action": "transform",
"templateId": "stripe-to-quickbooks-customer",
"sourceData": "{{steps.get-customer.output}}"
}
}Transform Output
Transform steps output detailed reports:
{
"transformed": {
"customerId": "cus_abc123",
"email": "customer@example.com",
"name": {
"first": "John",
"last": "Doe"
}
},
"mappingReport": {
"successful": ["customerId", "email", "name.first", "name.last"],
"failed": [],
"defaultsUsed": ["phone"],
"skipped": []
},
"normalizationReport": {
"normalized": ["email"],
"issues": []
},
"validationErrors": [],
"isValid": true,
"metadata": {
"sourceFieldCount": 5,
"targetFieldCount": 4,
"transformDurationMs": 12
}
}Output Fields
| Field | Description |
|---|---|
transformed | The transformed data matching target schema |
mappingReport | Report of field mapping operations |
normalizationReport | Report of normalization operations |
validationErrors | List of validation errors |
isValid | Whether the transformation is valid |
metadata | Transformation statistics |
Common Patterns
Data Mapping Between Systems
Map Stripe customer to QuickBooks format:
{
"id": "map-to-qb",
"type": "transform",
"name": "Map to QuickBooks",
"config": {
"action": "map",
"sourceData": "{{steps.stripe-customer.output}}",
"mappingRules": [
{
"source": "name",
"target": "DisplayName",
"transform": "titlecase"
},
{
"source": "email",
"target": "PrimaryEmailAddr.Address",
"transform": "lowercase"
},
{
"source": "phone",
"target": "PrimaryPhone.FreeFormNumber"
},
{
"source": "address.line1",
"target": "BillAddr.Line1"
},
{
"source": "address.city",
"target": "BillAddr.City"
},
{
"source": "address.state",
"target": "BillAddr.CountrySubDivisionCode",
"transform": "uppercase"
},
{
"source": "address.postal_code",
"target": "BillAddr.PostalCode"
}
]
}
}Data Cleanup and Standardization
{
"id": "cleanup-data",
"type": "transform",
"name": "Clean Customer Data",
"config": {
"action": "normalize",
"sourceData": "{{input}}",
"normalizations": [
{
"field": "email",
"type": "email"
},
{
"field": "phone",
"type": "phone"
},
{
"field": "name",
"type": "name"
},
{
"field": "created_at",
"type": "date",
"format": "ISO8601"
}
]
}
}Validation Before API Call
{
"id": "validate-invoice",
"type": "transform",
"name": "Validate Invoice Data",
"config": {
"action": "validate",
"sourceData": "{{input}}",
"validationRules": [
{
"field": "customer_id",
"rule": "required",
"message": "Customer ID is required"
},
{
"field": "customer_id",
"rule": "pattern",
"value": "^cus_",
"message": "Invalid customer ID format"
},
{
"field": "line_items",
"rule": "required",
"message": "At least one line item is required"
},
{
"field": "amount",
"rule": "range",
"value": { "min": 100 },
"message": "Amount must be at least $1.00"
}
],
"strict": true
}
}Enriching Data
Add computed fields:
{
"id": "enrich-order",
"type": "transform",
"name": "Enrich Order Data",
"config": {
"action": "enrich",
"sourceData": "{{input}}",
"targetSchema": {
"type": "object",
"properties": {
"orderId": { "type": "string" },
"subtotal": { "type": "number" },
"tax": { "type": "number" },
"total": { "type": "number" },
"processedAt": { "type": "string" }
}
},
"mappingRules": [
{
"source": "id",
"target": "orderId"
},
{
"source": "amount",
"target": "subtotal",
"transform": "number"
}
],
"customInstructions": "Calculate tax at 8% and total. Add processedAt with current timestamp."
}
}Flattening Nested Data
{
"id": "flatten-contacts",
"type": "transform",
"name": "Flatten Contact List",
"config": {
"action": "transform",
"sourceData": "{{steps.fetch-accounts.output.accounts}}",
"mappingRules": [
{
"source": "contacts",
"target": "allContacts",
"transform": "flatten"
}
]
}
}Strict Mode
Enable strict mode to fail on any transformation error:
{
"config": {
"action": "transform",
"sourceData": "{{input}}",
"mappingRules": [...],
"strict": true
}
}In strict mode:
- Missing required fields cause failure
- Transformation errors stop execution
- Validation errors cause failure
Without strict mode (default):
- Missing fields use defaults or are skipped
- Transformation errors are logged but continue
- Validation errors are reported but don't stop execution
Troubleshooting
Mapping Not Working
- Check field paths - Use dot notation for nested fields
- Verify source data - Add a debug transform to inspect data
- Check transform type - Ensure correct transform for the data type
Data Type Issues
- Use explicit transforms - Add
number,string,booleantransforms - Check for null values - Use
defaultfor missing fields - Validate input - Add validation step before transform
Performance
- Minimize rules - Combine mappings where possible
- Use templates - Pre-built templates are optimized
- Skip unnecessary fields - Only map fields you need