Data Mapping
Master input and output mapping between workflow steps using template syntax, nested property access, and data transformations.
Overview
Data mapping enables information flow through workflows. Each step can reference outputs from previous steps, chain inputs, or static values using template syntax.
The chain maintains a context object that accumulates all outputs as steps execute. Any step can access any data in this context.
Template Syntax
Use double curly braces {{key}} to reference values from the chain context:
{
"inputMapping": {
"userMessage": "{{message}}",
"sentiment": "{{sentiment_analysis.sentiment}}",
"confidence": "{{sentiment_analysis.confidence}}"
}
}What Can Be Referenced
- Chain inputs - Any input provided when the chain was executed
- Step outputs - Outputs from any previous step (accessed via outputKey)
- Nested properties - Use dot notation to access nested object properties
- Array elements - Use bracket notation to access array elements
Context Building
As the chain executes, the context object grows with each step's output:
// Initial chain input
{
"userMessage": "I'm very unhappy with my order",
"userId": "user_123"
}
// After step 0 (sentiment analysis) with outputKey "sentiment"
{
"userMessage": "I'm very unhappy with my order",
"userId": "user_123",
"sentiment": {
"sentiment": "Negative",
"confidence": 0.92,
"keyPhrases": ["very unhappy", "order"]
}
}
// After step 1 (user lookup) with outputKey "user"
{
"userMessage": "I'm very unhappy with my order",
"userId": "user_123",
"sentiment": { ... },
"user": {
"id": "user_123",
"name": "John Doe",
"email": "john@example.com",
"tier": "premium"
}
}
// After step 2 (generate response) with outputKey "response"
{
"userMessage": "I'm very unhappy with my order",
"userId": "user_123",
"sentiment": { ... },
"user": { ... },
"response": "Dear John, I sincerely apologize for your experience..."
}outputKey, not modify existing ones.Nested Property Access
Use dot notation to access nested object properties:
{
"inputMapping": {
"userName": "{{user.name}}",
"userEmail": "{{user.email}}",
"sentimentScore": "{{sentiment.confidence}}",
"isHighPriority": "{{user.tier}}",
"deepProperty": "{{response.data.analysis.summary}}"
}
}Array Access
{
"inputMapping": {
"firstItem": "{{items[0]}}",
"secondItemName": "{{items[1].name}}",
"lastPurchaseDate": "{{purchases[-1].date}}",
"nestedArray": "{{data.orders[0].items[2].sku}}"
}
}null. Ensure data exists before referencing nested properties.Input Mapping Patterns
Direct Mapping
{
"inputMapping": {
"text": "{{userMessage}}"
}
}Renaming
{
"inputMapping": {
"customerMessage": "{{userMessage}}",
"customerName": "{{user.name}}"
}
}Combining Multiple Sources
{
"inputMapping": {
"message": "{{userMessage}}",
"sentiment": "{{sentiment_analysis.sentiment}}",
"confidence": "{{sentiment_analysis.confidence}}",
"userName": "{{user.name}}",
"userTier": "{{user.tier}}",
"context": "{{previousResponse}}"
}
}Passing Entire Objects
{
"inputMapping": {
"user": "{{user}}",
"sentimentData": "{{sentiment_analysis}}",
"orderDetails": "{{api_response.data}}"
}
}Static Values
{
"inputMapping": {
"message": "{{userMessage}}",
"language": "en",
"maxLength": 500,
"includeReasoning": true
}
}inputMapping. Static values don't use the {{...}} syntax.Output Keys
The outputKey determines how a step's output is stored in the context:
{
"order": 0,
"type": "PROMPT",
"promptId": "sentiment-analyzer",
"inputMapping": { "text": "{{userMessage}}" },
"outputKey": "sentiment_analysis"
}This step's output will be accessible as {{sentiment_analysis}} in subsequent steps.
Naming Conventions
- Use snake_case - sentiment_analysis, user_data, api_response
- Be descriptive - What does this output contain?
- Avoid generic names - Don't use "result", "output", "data"
- Indicate the content - sentiment_analysis, order_details, recommendations
Overwriting Output Keys
[
{
"order": 2,
"type": "PROMPT",
"promptId": "apology-response",
"tags": ["negative_path"],
"outputKey": "response"
},
{
"order": 3,
"type": "PROMPT",
"promptId": "friendly-response",
"tags": ["positive_path"],
"outputKey": "response"
}
]Multiple steps can use the same outputKey. The last step to execute (based on conditional logic) will set the final value. This is useful for conditional branches that should all produce the same output type.
API Call Mapping
API call steps support template variables in URLs, headers, query parameters, and request bodies:
{
"order": 1,
"type": "API_CALL",
"config": {
"url": "https://api.example.com/users/{{userId}}/orders",
"method": "POST",
"headers": {
"Authorization": "Bearer {{apiKey}}",
"Content-Type": "application/json",
"X-User-Tier": "{{user.tier}}"
},
"queryParams": {
"limit": "10",
"sentiment": "{{sentiment_analysis.sentiment}}"
},
"body": {
"userId": "{{userId}}",
"message": "{{userMessage}}",
"sentiment": {
"value": "{{sentiment_analysis.sentiment}}",
"confidence": "{{sentiment_analysis.confidence}}"
},
"timestamp": "{{now}}"
}
},
"outputKey": "api_response"
}Response Data Access
API responses are automatically parsed as JSON and stored with the output key. Access response data using dot notation:
// API response stored in "api_response":
{
"status": "success",
"data": {
"ticketId": "TKT-12345",
"assignedTo": "support-team",
"priority": "high"
},
"meta": {
"processingTime": 150
}
}
// Reference in next step:
{
"inputMapping": {
"ticketId": "{{api_response.data.ticketId}}",
"assignee": "{{api_response.data.assignedTo}}",
"priority": "{{api_response.data.priority}}"
}
}Transform Steps for Data Manipulation
Use TRANSFORM steps when simple template mapping isn't enough:
Merging Multiple Objects
{
"order": 5,
"type": "TRANSFORM",
"config": {
"operation": "merge",
"inputs": [
"{{user}}",
"{{sentiment_analysis}}",
"{{api_response.data}}"
]
},
"outputKey": "enriched_data"
}
// Result stored in "enriched_data":
{
"id": "user_123",
"name": "John Doe",
"email": "john@example.com",
"sentiment": "Negative",
"confidence": 0.92,
"ticketId": "TKT-12345"
}Extracting Specific Fields
{
"order": 2,
"type": "TRANSFORM",
"config": {
"operation": "extract",
"input": "{{api_response}}",
"fields": ["data.userId", "data.email", "status", "meta.processingTime"]
},
"outputKey": "extracted_data"
}
// Result:
{
"userId": "user_123",
"email": "john@example.com",
"status": "success",
"processingTime": 150
}String Formatting
{
"order": 3,
"type": "TRANSFORM",
"config": {
"operation": "format",
"template": "User {{user.name}} ({{user.email}}) - Sentiment: {{sentiment.sentiment}} ({{sentiment.confidence}})",
"inputs": {
"user": "{{user}}",
"sentiment": "{{sentiment_analysis}}"
}
},
"outputKey": "formatted_summary"
}
// Result:
"User John Doe (john@example.com) - Sentiment: Negative (0.92)"Calculations
{
"order": 4,
"type": "TRANSFORM",
"config": {
"operation": "calculate",
"expression": "({{order.subtotal}} + {{order.tax}}) * (1 - {{user.discountRate}})",
"inputs": {
"order": "{{api_response.data}}",
"user": "{{user}}"
}
},
"outputKey": "final_total"
}Common Patterns
Passing Context Through Workflows
[
{
"order": 0,
"type": "API_CALL",
"config": {
"url": "https://api.example.com/users/{{userId}}"
},
"outputKey": "user"
},
{
"order": 1,
"type": "PROMPT",
"promptId": "analyze-sentiment",
"inputMapping": {
"text": "{{userMessage}}"
},
"outputKey": "sentiment"
},
{
"order": 2,
"type": "TRANSFORM",
"config": {
"operation": "merge",
"inputs": ["{{user}}", "{{sentiment}}"]
},
"outputKey": "context"
},
{
"order": 3,
"type": "PROMPT",
"promptId": "generate-response",
"inputMapping": {
"message": "{{userMessage}}",
"context": "{{context}}"
},
"outputKey": "response"
}
]Conditional Data Selection
[
{
"order": 0,
"type": "CONDITION",
"config": {
"condition": "{{user.tier}} == 'premium'",
"trueTag": "premium",
"falseTag": "standard"
}
},
{
"order": 1,
"type": "API_CALL",
"tags": ["premium"],
"config": {
"url": "https://api.example.com/premium-features/{{userId}}"
},
"outputKey": "features"
},
{
"order": 2,
"type": "API_CALL",
"tags": ["standard"],
"config": {
"url": "https://api.example.com/standard-features/{{userId}}"
},
"outputKey": "features"
},
{
"order": 3,
"type": "PROMPT",
"promptId": "generate-response",
"inputMapping": {
"features": "{{features}}",
"user": "{{user}}"
},
"outputKey": "response"
}
]Both branches write to the same features key, so step 3 can always reference {{features}} regardless of which branch executed.
Error Handling
Missing Data
// If a referenced key doesn't exist:
{
"inputMapping": {
"name": "{{user.name}}",
"nonexistent": "{{does_not_exist}}"
}
}
// Results in:
{
"name": "John Doe",
"nonexistent": null
}null rather than causing an error. Validate data exists before using it in critical operations.Safe Navigation
// Instead of:
"{{api_response.data.user.profile.avatar}}"
// Use a CONDITION step to check existence first:
{
"order": 1,
"type": "CONDITION",
"config": {
"condition": "{{api_response.data}} != null",
"trueTag": "has_data",
"falseTag": "no_data"
}
}