Workflow Steps

Deep dive into all chain step types including prompts, API calls, conditionals, and data transformations.

Overview

Chain steps are the building blocks of workflows. Each step performs a specific action and can pass its output to subsequent steps. Steps execute in order unless conditional logic alters the flow.

ParameterTypeDescription
PROMPTStepTypeExecute an AI prompt with dynamic inputs
API_CALLStepTypeCall an external API endpoint
CONDITIONStepTypeBranch execution based on a condition
TRANSFORMStepTypeTransform or manipulate data
MERGEStepTypeCombine outputs from multiple steps
MEMORYStepTypeStore and retrieve persistent data

Step Anatomy

Every step has the following core properties:

ParameterTypeDescription
order
required
numberExecution order (0-indexed). Steps execute in ascending order
type
required
StepTypeThe type of step (PROMPT, API_CALL, CONDITION, TRANSFORM, MERGE, MEMORY)
configJSONStep-specific configuration (varies by type)
inputMappingJSONHow to map previous outputs to this step's inputs
outputKeystringKey to store this step's output for later steps to reference
tagsstring[]Tags for conditional execution (used with CONDITION steps)

PROMPT Steps

Prompt steps execute an AI prompt with inputs from the chain context. They are the most common step type and the core of most workflows.

Basic Structure

{
  "order": 0,
  "type": "PROMPT",
  "promptId": "cm1234567890",
  "inputMapping": {
    "text": "{{userMessage}}",
    "context": "{{previousStep.summary}}"
  },
  "outputKey": "analysis_result",
  "config": {}
}

Configuration Options

ParameterTypeDescription
promptId
required
IDID of the prompt to execute
versionnumberSpecific prompt version to use (defaults to latest)
overrideParamsJSONOverride model parameters (temperature, max_tokens) for this execution

Override Model Parameters

{
  "order": 0,
  "type": "PROMPT",
  "promptId": "cm1234567890",
  "inputMapping": {
    "text": "{{userMessage}}"
  },
  "outputKey": "creative_response",
  "config": {
    "overrideParams": {
      "temperature": 0.9,
      "max_tokens": 500
    }
  }
}
Override params apply only to this step execution. The prompt's default parameters remain unchanged for other uses.

Example Use Cases

Sentiment Analysis

Analyze customer messages to determine sentiment before routing or responding

Content Generation

Generate personalized responses based on user context and previous step outputs

Data Extraction

Extract structured data from unstructured text for use in subsequent steps

API_CALL Steps

API call steps make HTTP requests to external services. They enable integration with third-party APIs, databases, or internal services.

Basic Structure

{
  "order": 1,
  "type": "API_CALL",
  "config": {
    "url": "https://api.example.com/v1/users",
    "method": "POST",
    "headers": {
      "Authorization": "Bearer {{apiKey}}",
      "Content-Type": "application/json"
    },
    "body": {
      "email": "{{userEmail}}",
      "sentiment": "{{sentiment_analysis.sentiment}}",
      "message": "{{userMessage}}"
    },
    "timeout": 5000
  },
  "outputKey": "api_response"
}

Configuration Options

ParameterTypeDescription
url
required
stringAPI endpoint URL (supports template variables)
method
required
stringHTTP method (GET, POST, PUT, PATCH, DELETE)
headersobjectRequest headers (supports template variables)
bodyobjectRequest body for POST/PUT/PATCH (supports template variables)
queryParamsobjectURL query parameters (supports template variables)
timeoutnumberRequest timeout in milliseconds (default: 10000)

GET Request Example

{
  "order": 0,
  "type": "API_CALL",
  "config": {
    "url": "https://api.example.com/v1/users/{{userId}}",
    "method": "GET",
    "headers": {
      "Authorization": "Bearer {{apiKey}}"
    },
    "queryParams": {
      "include": "profile,settings"
    }
  },
  "outputKey": "user_data"
}

Response Handling

The API response is automatically parsed as JSON and stored in the output key. You can reference nested response data in subsequent steps:

// API response stored in "api_response":
{
  "status": "success",
  "data": {
    "ticketId": "TKT-12345",
    "assignedTo": "support-team"
  }
}

// Reference in next step:
{
  "inputMapping": {
    "ticketId": "{{api_response.data.ticketId}}",
    "assignee": "{{api_response.data.assignedTo}}"
  }
}
API calls will fail the chain if the request returns a non-2xx status code or times out. Implement proper error handling in your workflow design.

Example Use Cases

CRM Integration

Create or update customer records in Salesforce, HubSpot, or custom CRMs

Database Queries

Fetch user data, preferences, or historical information via REST APIs

Notification Services

Send emails, SMS, or push notifications via third-party services

CONDITION Steps

Condition steps enable branching logic in workflows. They evaluate expressions and tag subsequent steps for conditional execution.

Basic Structure

{
  "order": 2,
  "type": "CONDITION",
  "config": {
    "condition": "{{sentiment_analysis.sentiment}} == 'Negative'",
    "trueTag": "escalate",
    "falseTag": "standard"
  }
}

Configuration Options

ParameterTypeDescription
condition
required
stringBoolean expression to evaluate (supports ==, !=, >, <, >=, <=, &&, ||)
trueTag
required
stringTag for steps to execute when condition is true
falseTag
required
stringTag for steps to execute when condition is false

Supported Operators

ParameterTypeDescription
==operatorEquality comparison
!=operatorInequality comparison
>operatorGreater than
<operatorLess than
>=operatorGreater than or equal
<=operatorLess than or equal
&&operatorLogical AND
||operatorLogical OR

Complex Conditions

{
  "order": 1,
  "type": "CONDITION",
  "config": {
    "condition": "{{sentiment.confidence}} >= 0.8 && {{sentiment.sentiment}} == 'Negative'",
    "trueTag": "high_priority_escalation",
    "falseTag": "normal_processing"
  }
}

Tagging Steps for Conditional Execution

[
  {
    "order": 0,
    "type": "PROMPT",
    "promptId": "sentiment-analyzer",
    "inputMapping": { "text": "{{message}}" },
    "outputKey": "sentiment"
  },
  {
    "order": 1,
    "type": "CONDITION",
    "config": {
      "condition": "{{sentiment.sentiment}} == 'Negative'",
      "trueTag": "escalate",
      "falseTag": "standard"
    }
  },
  {
    "order": 2,
    "type": "PROMPT",
    "promptId": "escalation-response",
    "tags": ["escalate"],
    "inputMapping": { "message": "{{message}}", "sentiment": "{{sentiment}}" },
    "outputKey": "response"
  },
  {
    "order": 3,
    "type": "PROMPT",
    "promptId": "standard-response",
    "tags": ["standard"],
    "inputMapping": { "message": "{{message}}" },
    "outputKey": "response"
  }
]
Steps without tags always execute. Steps with tags only execute when their tag matches the condition result. See the Conditionals guide for more details.

TRANSFORM Steps

Transform steps manipulate data between steps. They can format strings, perform calculations, merge objects, or restructure data.

Basic Structure

{
  "order": 1,
  "type": "TRANSFORM",
  "config": {
    "operation": "merge",
    "inputs": ["{{user_data}}", "{{sentiment_analysis}}"]
  },
  "outputKey": "combined_data"
}

Supported Operations

ParameterTypeDescription
mergeoperationMerge multiple objects into one
extractoperationExtract specific fields from an object
formatoperationFormat strings with template syntax
calculateoperationPerform mathematical calculations
array_mapoperationTransform each element in an array
array_filteroperationFilter array elements by condition

Merge Operation

{
  "order": 2,
  "type": "TRANSFORM",
  "config": {
    "operation": "merge",
    "inputs": [
      "{{user_profile}}",
      "{{sentiment_data}}",
      { "processedAt": "{{now}}" }
    ]
  },
  "outputKey": "enriched_data"
}

Extract Operation

{
  "order": 1,
  "type": "TRANSFORM",
  "config": {
    "operation": "extract",
    "input": "{{api_response}}",
    "fields": ["data.userId", "data.email", "status"]
  },
  "outputKey": "user_info"
}

Format Operation

{
  "order": 1,
  "type": "TRANSFORM",
  "config": {
    "operation": "format",
    "template": "User {{user.name}} ({{user.email}}) - Sentiment: {{sentiment.result}}",
    "inputs": {
      "user": "{{user_data}}",
      "sentiment": "{{sentiment_analysis}}"
    }
  },
  "outputKey": "formatted_summary"
}

Calculate Operation

{
  "order": 3,
  "type": "TRANSFORM",
  "config": {
    "operation": "calculate",
    "expression": "{{price}} * {{quantity}} * (1 - {{discount}})",
    "inputs": {
      "price": "{{product.price}}",
      "quantity": "{{order.quantity}}",
      "discount": "{{user.discountRate}}"
    }
  },
  "outputKey": "total_cost"
}

MERGE Steps

Merge steps combine outputs from multiple previous steps into a single unified object or array. This is useful when you need to consolidate data from parallel operations or combine results for downstream processing.

Basic Structure

{
  "order": 3,
  "type": "MERGE",
  "config": {
    "sources": ["sentiment_analysis", "user_profile", "risk_score"],
    "strategy": "object"
  },
  "outputKey": "combined_context"
}

Configuration Options

ParameterTypeDescription
sources
required
string[]Array of output keys from previous steps to merge
strategy
required
stringMerge strategy: "object" (merge properties) or "array" (collect as array)

Object Merge Strategy

The object strategy merges all properties from the specified sources into a single object. If sources have overlapping properties, later values override earlier ones.

{
  "order": 3,
  "type": "MERGE",
  "config": {
    "sources": ["user_data", "sentiment_result", "risk_assessment"],
    "strategy": "object"
  },
  "outputKey": "enriched_profile"
}

// Input:
// user_data: { "userId": "123", "name": "John" }
// sentiment_result: { "sentiment": "positive", "confidence": 0.95 }
// risk_assessment: { "riskScore": 0.12, "category": "low" }

// Output (enriched_profile):
{
  "userId": "123",
  "name": "John",
  "sentiment": "positive",
  "confidence": 0.95,
  "riskScore": 0.12,
  "category": "low"
}

Array Collection Strategy

The array strategy collects all source values into an array, preserving each source as a separate element.

{
  "order": 4,
  "type": "MERGE",
  "config": {
    "sources": ["analysis_1", "analysis_2", "analysis_3"],
    "strategy": "array"
  },
  "outputKey": "all_analyses"
}

// Input:
// analysis_1: { "result": "positive" }
// analysis_2: { "result": "neutral" }
// analysis_3: { "result": "positive" }

// Output (all_analyses):
[
  { "result": "positive" },
  { "result": "neutral" },
  { "result": "positive" }
]
MERGE steps are particularly useful when you have parallel processing paths that need to be consolidated before a final step. For example, analyzing text with multiple prompts and combining the results.

Example Use Cases

Multi-Model Analysis

Run the same input through multiple AI models and merge their outputs for comparison

Context Enrichment

Combine user data from CRM, sentiment analysis, and historical interactions into a unified context

Parallel Processing

Collect results from multiple API calls or analyses that run independently

MEMORY Steps

Memory steps provide persistent, user-scoped key-value storage with versioning and automatic compression. Use memory to maintain context across multiple chain executions, store user preferences, or track conversation history.

Basic Structure

{
  "order": 2,
  "type": "MEMORY",
  "config": {
    "operation": "set",
    "key": "customer.sentiment_history"
  },
  "inputMapping": {
    "value": "{{sentiment_analysis}}"
  },
  "outputKey": "memory_result"
}

Memory Operations

ParameterTypeDescription
getoperationRetrieve a single memory value by key
setoperationStore or update a memory value with optional compression
queryoperationSearch memories using pattern matching (e.g., customer.*)
deleteoperationDelete all versions of a memory key
getKeysoperationList all available memory keys
getHistoryoperationGet version history for a memory key

GET Operation

Retrieve a stored memory value by its key. Returns null if the key does not exist.

{
  "order": 0,
  "type": "MEMORY",
  "config": {
    "operation": "get",
    "key": "customer.profile"
  },
  "outputKey": "customer_profile"
}

// Output:
{
  "value": {
    "name": "John Doe",
    "preferences": ["email", "sms"],
    "lastInteraction": "2025-01-15"
  },
  "version": 3,
  "compressed": false
}

SET Operation

Store a value in memory with optional automatic compression. If the content exceeds the specified token limit, it will be automatically summarized using an LLM.

{
  "order": 1,
  "type": "MEMORY",
  "config": {
    "operation": "set",
    "key": "customer.sentiment",
    "maxTokens": 500
  },
  "inputMapping": {
    "value": "{{sentiment_analysis}}"
  },
  "outputKey": "memory_result"
}

// Output:
{
  "id": "mem_abc123",
  "key": "customer.sentiment",
  "version": 2,
  "compressed": false,
  "tokenCount": 45
}
ParameterTypeDescription
key
required
stringHierarchical key using dot notation (e.g., customer.profile.preferences)
value
required
anyThe value to store (provided via inputMapping)
maxTokensnumberMaximum tokens before compression (default: 500)
summarizationPromptIdIDCustom prompt for summarization (optional)

QUERY Operation

Search for memories using wildcard patterns. This is useful for retrieving related memories or exploring what data is stored under a namespace.

{
  "order": 0,
  "type": "MEMORY",
  "config": {
    "operation": "query",
    "keyPattern": "customer.*",
    "limit": 10
  },
  "outputKey": "customer_memories"
}

// Output:
{
  "results": [
    {
      "key": "customer.profile",
      "value": { "name": "John", "email": "john@example.com" },
      "version": 1
    },
    {
      "key": "customer.sentiment",
      "value": { "sentiment": "positive", "confidence": 0.95 },
      "version": 2
    },
    {
      "key": "customer.preferences",
      "value": ["email", "sms"],
      "version": 1
    }
  ],
  "count": 3
}
ParameterTypeDescription
keyPattern
required
stringPattern with wildcards (e.g., customer.* matches customer.profile, customer.sentiment)
limitnumberMaximum number of memories to return (default: 50)
valueTypestringFilter by value type: string, object, array, number

Memory Keys and Namespacing

Memory keys use dot notation to create hierarchical namespaces. This helps organize related data and makes pattern-based queries more powerful.

// Good key structure examples:
"customer.profile"              // Customer profile data
"customer.sentiment_history"    // Historical sentiment data
"customer.preferences.contact"  // Contact preferences
"session.context"               // Current session context
"analytics.summary"             // Analytics summaries

// Query examples:
"customer.*"                    // All customer data
"customer.preferences.*"        // All customer preferences
"*.summary"                     // All summaries across namespaces

Automatic Compression

When memory content exceeds the specified maxTokens, the system automatically compresses it using an LLM-based summarization prompt. This keeps memory storage efficient while preserving important information.

{
  "order": 1,
  "type": "MEMORY",
  "config": {
    "operation": "set",
    "key": "conversation.history",
    "maxTokens": 500,
    "summarizationPromptId": "custom-summarizer"
  },
  "inputMapping": {
    "value": "{{conversation_transcript}}"
  },
  "outputKey": "stored_memory"
}

// If conversation_transcript is 2000 tokens:
// - System detects it exceeds maxTokens (500)
// - Automatically summarizes using the specified prompt
// - Stores compressed version
// - Sets compressed: true in metadata
All memory operations are user-scoped. Each user has their own isolated memory namespace, ensuring data privacy and security. Memories are automatically versioned, allowing you to track how data evolves over time.

Example Use Cases

Conversation History

Store conversation context across multiple interactions to provide personalized responses

User Preferences

Remember user preferences, settings, and behavior patterns for customized experiences

Analytics Tracking

Track sentiment trends, interaction patterns, or any time-series data per user

Progressive Profiling

Build rich user profiles over time by accumulating insights from each interaction

DELETE Operation

{
  "order": 5,
  "type": "MEMORY",
  "config": {
    "operation": "delete",
    "key": "customer.old_data"
  },
  "outputKey": "delete_result"
}

// Output:
{
  "success": true,
  "deletedCount": 3  // Number of versions deleted
}

GET KEYS Operation

{
  "order": 0,
  "type": "MEMORY",
  "config": {
    "operation": "getKeys",
    "pattern": "customer.*"
  },
  "outputKey": "available_keys"
}

// Output:
{
  "keys": [
    "customer.profile",
    "customer.sentiment",
    "customer.preferences",
    "customer.history"
  ]
}

GET HISTORY Operation

{
  "order": 0,
  "type": "MEMORY",
  "config": {
    "operation": "getHistory",
    "key": "customer.sentiment"
  },
  "outputKey": "sentiment_history"
}

// Output:
{
  "versions": [
    {
      "version": 3,
      "value": { "sentiment": "positive", "confidence": 0.95 },
      "createdAt": "2025-01-15T10:30:00Z",
      "compressed": false
    },
    {
      "version": 2,
      "value": { "sentiment": "neutral", "confidence": 0.87 },
      "createdAt": "2025-01-10T14:20:00Z",
      "compressed": false
    },
    {
      "version": 1,
      "value": { "sentiment": "negative", "confidence": 0.91 },
      "createdAt": "2025-01-05T09:15:00Z",
      "compressed": false
    }
  ]
}

Step Execution Flow

Steps execute in order based on their order property:

  1. Chain receives input and initializes context
  2. Steps are sorted by order (0, 1, 2, ...)
  3. For each step:
    • Check if step has tags (skip if tags don't match active condition)
    • Resolve input mapping from context
    • Execute step (prompt, API call, etc.)
    • Store output in context with outputKey
    • Record metrics (latency, cost, status)
  4. Return final output (usually from last executed step)
If a step fails, the chain execution stops immediately and returns an error. Previous successful steps are still recorded in the execution history.

Best Practices

Use Descriptive Output Keys - Name output keys clearly (e.g., "sentiment_analysis" not "step1") to make input mappings more readable.
Test Steps Independently - Before adding steps to a chain, test prompts independently to ensure they work correctly.
Order Steps Logically - Use order values that leave room for insertion (0, 10, 20) rather than consecutive numbers (0, 1, 2).
Handle API Errors - Design workflows to gracefully handle API call failures. Consider using conditional logic to provide fallbacks.
Avoid Circular References - Don't create input mappings that reference the current step's output. This will cause execution errors.

Next Steps

Conditional Logic

Learn advanced branching and conditional execution

Conditionals →

Data Mapping

Master input/output mapping between steps

Data Mapping →