Core Concepts
Understand the fundamental building blocks of n8n workflows. This guide explains how nodes, workflows, triggers, credentials, and expressions work together to create powerful automations.
Workflows
A workflow is a collection of connected nodes that execute in a specific order to accomplish a task. Workflows are represented as JSON objects in n8n.
Workflow Structure
Based on the source code (packages/workflow/src/interfaces.ts), a workflow consists of:
interface IWorkflowBase {
id : string ; // Unique workflow identifier
name : string ; // Workflow name
active : boolean ; // Whether workflow is activated
nodes : INode []; // Array of nodes
connections : IConnections ; // Node connections
settings ?: IWorkflowSettings ; // Workflow-specific settings
staticData ?: IDataObject ; // Persistent workflow data
pinData ?: IPinData ; // Pinned test data
}
Workflow Execution Modes
n8n supports several execution modes:
Manual Triggered manually via the UI or API
Trigger Activated by triggers (webhooks, schedules, polling)
Error Executed when an error workflow is triggered
Webhook Responds to incoming webhook requests
Example Workflow JSON
{
"name" : "Data Sync Workflow" ,
"nodes" : [
{
"id" : "schedule-1" ,
"name" : "Schedule Trigger" ,
"type" : "n8n-nodes-base.scheduleTrigger" ,
"typeVersion" : 1 ,
"position" : [ 250 , 300 ],
"parameters" : {
"rule" : {
"interval" : [{ "field" : "hours" , "hoursInterval" : 1 }]
}
}
},
{
"id" : "http-1" ,
"name" : "Fetch Data" ,
"type" : "n8n-nodes-base.httpRequest" ,
"typeVersion" : 3 ,
"position" : [ 450 , 300 ],
"parameters" : {
"method" : "GET" ,
"url" : "https://api.example.com/data"
}
}
],
"connections" : {
"Schedule Trigger" : {
"main" : [[{ "node" : "Fetch Data" , "type" : "main" , "index" : 0 }]]
}
},
"active" : true
}
Nodes
A node is a single step in a workflow that performs a specific action. Nodes are the building blocks of automations.
Node Structure
From the source code, each node has:
interface INode {
id : string ; // Unique node instance ID
name : string ; // Node instance name
type : string ; // Node type (e.g., "n8n-nodes-base.httpRequest")
typeVersion : number ; // Node version
position : [ number , number ]; // Canvas position [x, y]
parameters : INodeParameters ; // Node configuration
credentials ?: INodeCredentials ; // Attached credentials
disabled ?: boolean ; // Whether node is disabled
continueOnFail ?: boolean ; // Continue workflow on error
}
Node Types
n8n has several categories of nodes:
1. Trigger Nodes
Trigger nodes start workflow execution:
Webhook Trigger
Schedule Trigger
Polling Trigger
Listens for incoming HTTP requests // From packages/nodes-base/nodes/Webhook/
{
"name" : "Webhook" ,
"type" : "n8n-nodes-base.webhook" ,
"parameters" : {
"path" : "my-webhook" ,
"httpMethod" : "POST" ,
"responseMode" : "onReceived"
}
}
Accessible at: https://your-domain.com/webhook/my-webhook Executes on a schedule {
"name" : "Schedule Trigger" ,
"type" : "n8n-nodes-base.scheduleTrigger" ,
"parameters" : {
"rule" : {
"interval" : [
{ "field" : "minutes" , "minutesInterval" : 15 }
]
}
}
}
Periodically checks for new data {
"name" : "Gmail Trigger" ,
"type" : "n8n-nodes-base.gmailTrigger" ,
"parameters" : {
"event" : "messageReceived" ,
"filters" : {
"subject" : "Invoice"
}
}
}
Polling triggers use getWorkflowStaticData('node') to persist state between polls.
2. Action Nodes
Perform operations with services:
{
"name" : "Send Email" ,
"type" : "n8n-nodes-base.emailSend" ,
"parameters" : {
"fromEmail" : "sender@example.com" ,
"toEmail" : "={{ $json.email }}" ,
"subject" : "Order Confirmation" ,
"text" : "Your order #={{ $json.orderId }} is confirmed"
}
}
3. Core Nodes
Built-in utility nodes:
Code Execute JavaScript or Python
HTTP Request Make API calls to any service
Set Transform and manipulate data
Switch Multi-way branching
Merge Combine data from multiple branches
Node Implementation
Nodes implement the INodeType interface (from packages/nodes-base/):
// Example from packages/nodes-base/nodes/HttpRequest/
export class HttpRequestV3 implements INodeType {
description : INodeTypeDescription = {
displayName: 'HTTP Request' ,
name: 'httpRequest' ,
icon: 'fa:at' ,
group: [ 'output' ],
version: [ 3 , 4 , 4.1 , 4.2 , 4.3 , 4.4 ],
description: 'Makes an HTTP request' ,
defaults: {
name: 'HTTP Request' ,
color: '#0004F5' ,
},
inputs: [ NodeConnectionTypes . Main ],
outputs: [ NodeConnectionTypes . Main ],
properties: [
{
displayName: 'Method' ,
name: 'method' ,
type: 'options' ,
options: [
{ name: 'GET' , value: 'GET' },
{ name: 'POST' , value: 'POST' },
{ name: 'PUT' , value: 'PUT' },
{ name: 'DELETE' , value: 'DELETE' }
],
default: 'GET'
},
{
displayName: 'URL' ,
name: 'url' ,
type: 'string' ,
default: '' ,
required: true
}
]
};
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [][]> {
const items = this . getInputData ();
const returnData : INodeExecutionData [] = [];
for ( let i = 0 ; i < items . length ; i ++ ) {
const method = this . getNodeParameter ( 'method' , i ) as string ;
const url = this . getNodeParameter ( 'url' , i ) as string ;
const response = await this . helpers . httpRequest ({
method ,
url
});
returnData . push ({ json: response });
}
return [ returnData ];
}
}
Connections
Connections define how data flows between nodes.
Connection Structure
interface IConnection {
node : string ; // Destination node name
type : NodeConnectionType ; // Connection type (usually "main")
index : number ; // Input/output index
}
interface IConnections {
[ sourceNodeName : string ] : {
[ connectionType : string ] : Array < IConnection [] | null >
}
}
Connection Types
main : Regular data flow (default)
ai_agent : AI agent connections
ai_tool : AI tool connections
ai_memory : AI memory connections
ai_document : AI document connections
Example Connections
{
"connections" : {
"Start" : {
"main" : [
[
{ "node" : "HTTP Request" , "type" : "main" , "index" : 0 }
]
]
},
"HTTP Request" : {
"main" : [
[
{ "node" : "IF" , "type" : "main" , "index" : 0 }
]
]
},
"IF" : {
"main" : [
[{ "node" : "Success Handler" , "type" : "main" , "index" : 0 }],
[{ "node" : "Error Handler" , "type" : "main" , "index" : 0 }]
]
}
}
}
The connections object is indexed by source node. To find parent nodes, use the mapConnectionsByDestination() utility from n8n-workflow.
Data Flow
Node Execution Data
Data passed between nodes follows this structure:
interface INodeExecutionData {
json : IDataObject ; // JSON data
binary ?: IBinaryKeyData ; // Binary files
pairedItem ?: IPairedItemData ; // Source item tracking
}
interface IBinaryData {
data : string ; // Base64 encoded or file ID
mimeType : string ; // MIME type
fileName ?: string ; // Original filename
fileExtension ?: string ; // File extension
fileSize ?: string ; // Human-readable size
bytes ?: number ; // Size in bytes
}
Processing Multiple Items
Nodes process items in batches:
// In a Code node
const items = $input . all ();
// Process each item
const results = items . map (( item , index ) => {
return {
json: {
originalId: item . json . id ,
processed: true ,
index: index
},
pairedItem: { item: index } // Track source item
};
});
return results ;
Expressions
Expressions allow dynamic data access using the ={{ }} syntax.
Accessing Data
Current Node
Previous Nodes
Workflow Data
// Access current item data
{{ $json . fieldName }}
{{ $json [ "field-with-dashes" ] }}
{{ $json . nested . field }}
// Access binary data
{{ $binary . fileName . data }}
{{ $binary . fileName . mimeType }}
Expression Functions
// String manipulation
{{ "hello" . toUpperCase () }}
{{ $json . email . toLowerCase () }}
{{ $json . text . trim () }}
// Date/Time
{{ $now }}
{{ $today }}
{{ DateTime . now (). toISO () }}
{{ DateTime . fromISO ( $json . date ). plus ({ days: 7 }). toISO () }}
// Math
{{ Math . round ( $json . price * 1.2 ) }}
{{ Math . max ( $json . value1 , $json . value2 ) }}
// Arrays
{{ $json . items . length }}
{{ $json . items . map ( i => i . name ). join ( ", " ) }}
{{ $json . items . filter ( i => i . active ) }}
// Conditionals
{{ $json . status === 'active' ? 'Active' : 'Inactive' }}
Expressions use JavaScript syntax and have access to built-in functions from Luxon (dates), lodash (utilities), and standard JavaScript.
Credentials
Credentials store authentication information securely.
Credential Structure
interface ICredentials {
id ?: string ;
name : string ; // User-defined credential name
type : string ; // Credential type
data ?: string ; // Encrypted credential data
}
interface INodeCredentialsDetails {
id ?: string ;
name : string ;
}
Using Credentials in Nodes
Nodes declare credential requirements:
{
credentials : [
{
name: 'httpBasicAuth' ,
required: true ,
displayOptions: {
show: {
authentication: [ 'basicAuth' ]
}
}
},
{
name: 'httpHeaderAuth' ,
required: true ,
displayOptions: {
show: {
authentication: [ 'headerAuth' ]
}
}
}
]
}
Accessing Credentials in Code
// In a Code node or custom node
const credentials = await this . getCredentials ( 'credentialType' );
const apiKey = credentials . apiKey ;
const apiUrl = credentials . apiUrl ;
// Use in HTTP request
const response = await this . helpers . httpRequestWithAuthentication (
'credentialType' ,
{
method: 'GET' ,
url: apiUrl
}
);
Common Credential Types
OAuth2 OAuth 2.0 authentication with automatic token refresh
API Key Simple API key authentication
Basic Auth Username and password authentication
Header Auth Custom header authentication
Error Handling
Continue on Fail
Control error behavior at the node level:
{
"name" : "HTTP Request" ,
"type" : "n8n-nodes-base.httpRequest" ,
"continueOnFail" : true ,
"parameters" : {
"url" : "https://api.example.com/data"
}
}
When continueOnFail is true, errors are passed to the next node instead of stopping execution.
Error Workflows
Trigger a separate workflow on error:
Create an error workflow with a “Error Trigger” node
In your main workflow settings, select the error workflow
Errors will trigger the error workflow with context:
{
"execution" : {
"id" : "123" ,
"mode" : "trigger" ,
"error" : {
"message" : "API request failed" ,
"node" : "HTTP Request"
}
},
"workflow" : {
"id" : "456" ,
"name" : "Main Workflow"
}
}
Retry on Fail
Configure automatic retries:
{
"name" : "HTTP Request" ,
"retryOnFail" : true ,
"maxTries" : 3 ,
"waitBetweenTries" : 1000 ,
"parameters" : {
"url" : "https://api.example.com/data"
}
}
Static Data
Persist data between workflow executions:
// In a Code node or trigger
const staticData = this . getWorkflowStaticData ( 'node' );
// Store last execution time
if ( ! staticData . lastRun ) {
staticData . lastRun = new Date (). toISOString ();
}
// Get items since last run
const newItems = items . filter ( item =>
new Date ( item . json . createdAt ) > new Date ( staticData . lastRun )
);
// Update last run time
staticData . lastRun = new Date (). toISOString ();
return newItems ;
Static data is stored in the database and persists across workflow executions. Don’t store large amounts of data.
Execution Context
Nodes have access to execution context through this:
interface IExecuteFunctions {
// Get input data
getInputData () : INodeExecutionData [];
// Get parameter value
getNodeParameter ( name : string , index : number ) : any ;
// Get credentials
getCredentials ( type : string ) : Promise < ICredentialDataDecryptedObject >;
// Helper functions
helpers : {
httpRequest ( options : IHttpRequestOptions ) : Promise < any >;
httpRequestWithAuthentication ( type : string , options ) : Promise < any >;
};
// Workflow static data
getWorkflowStaticData ( type : 'node' | 'global' ) : IDataObject ;
// Context
getExecutionId () : string ;
getWorkflow () : IWorkflow ;
getMode () : WorkflowExecuteMode ;
}
Best Practices
Error Handling Always configure error handling for production workflows using continueOnFail or error workflows
Expressions Use expressions for dynamic data instead of hardcoding values
Credentials Never hardcode credentials in workflows - always use credential system
Testing Test workflows manually before activating with real data
Documentation Use node notes to document complex logic
Modularity Break complex workflows into smaller, reusable workflows
Advanced Topics
Workflow Versioning
Workflows support versioning:
{
"versionId" : "abc123" , // Current version ID
"activeVersionId" : "abc123" // Active version ID
}
Pinned Data
Pin test data to nodes for consistent testing:
{
"pinData" : {
"HTTP Request" : [
{
"json" : {
"id" : 1 ,
"name" : "Test Item"
}
}
]
}
}
Sub-workflows
Call other workflows using the “Execute Workflow” node:
{
"name" : "Execute Workflow" ,
"type" : "n8n-nodes-base.executeWorkflow" ,
"parameters" : {
"workflowId" : "456" ,
"source" : "database"
}
}
Learning Resources
Quick Start Build your first workflow
Example Workflows Browse 900+ templates
Node Reference Documentation for all 400+ nodes
Community Forum Ask questions and share workflows
This documentation is based on n8n version 2.9.0. Core concepts are stable, but specific node implementations may vary by version.