n8n/packages/workflow/src/interfaces.ts
Michael Drury 68f856fb9d
feat(core): Add production n8n Chat channel infrastructure (no-changelog) (#39476)
Co-authored-by: Benjamin Schroth <benjamin@n8n.io>
2026-09-28 11:47:35 +00:00

4562 lines
142 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable @typescript-eslint/no-explicit-any */
import type { CallbackManager as CallbackManagerLC } from '@langchain/core/callbacks/manager';
import type { LogScope } from '@n8n/config';
import type { AxiosProxyConfig, GenericAbortSignal } from 'axios';
import type * as express from 'express';
import type FormData from 'form-data';
import type { PathLike } from 'fs';
import type { IncomingHttpHeaders } from 'http';
import type { AgentOptions } from 'https';
import type { JSONSchema7 } from 'json-schema';
import type { LookupFunction } from 'net';
import type { ReplyHeaders, RequestBodyMatcher, RequestHeaderMatcher } from 'nock';
import type { Client as SSHClient } from 'ssh2';
import type { Readable } from 'stream';
import type { SecureContextOptions } from 'tls';
import type { URLSearchParams } from 'url';
import type {
CODE_EXECUTION_MODES,
CODE_LANGUAGES,
LOG_LEVELS,
BINARY_MODE_COMBINED,
BINARY_MODE_SEPARATE,
} from './constants';
import type {
IDataTableProjectAggregateService,
IDataTableProjectService,
} from './data-table.types';
import type { ExecutionCancelledError } from './errors';
import type { ExpressionError } from './errors/expression.error';
import type { NodeApiError } from './errors/node-api.error';
import type { NodeOperationError } from './errors/node-operation.error';
import type { WorkflowActivationError } from './errors/workflow-activation.error';
import type { WorkflowOperationError } from './errors/workflow-operation.error';
import type {
IExecutionContext,
WorkflowExecuteModeValues as WorkflowExecuteMode,
} from './execution-context';
import type { ExecutionStatus } from './execution-status';
import type { IDeferredPromise } from '@n8n/utils/promise/deferred-promise';
import type { Result } from '@n8n/utils/result';
import type { Workflow } from './workflow';
import type { EnvProviderState } from './workflow-data-proxy-env-provider';
import type { IRunExecutionData } from './run-execution-data/run-execution-data';
export type { WorkflowExecuteModeValues as WorkflowExecuteMode } from './execution-context';
export interface IAdditionalCredentialOptions {
oauth2?: IOAuth2Options;
credentialsDecrypted?: ICredentialsDecrypted;
/**
* Status code(s) that trigger the generic preAuthentication refresh-and-resend in
* `httpRequestWithAuthentication` (non-OAuth1/OAuth2 credentials only). Defaults to 401;
* override when a gateway signals an expired token with a different status.
*/
preAuthenticationRetryStatusCode?: number | number[];
}
export type IAllExecuteFunctions =
| IExecuteFunctions
| IExecutePaginationFunctions
| IExecuteSingleFunctions
| ISupplyDataFunctions
| IHookFunctions
| ILoadOptionsFunctions
| IPollFunctions
| ITriggerFunctions
| IWebhookFunctions;
export type BinaryFileType = 'text' | 'json' | 'image' | 'audio' | 'video' | 'pdf' | 'html';
export interface IBinaryData {
[key: string]: string | number | undefined;
data: string;
mimeType: string;
fileType?: BinaryFileType;
fileName?: string;
directory?: string;
fileExtension?: string;
fileSize?: string;
bytes?: number;
id?: string;
}
// All properties in this interface except for
// "includeCredentialsOnRefreshOnBody" will get
// removed once we add the OAuth2 hooks to the
// credentials file.
export interface IOAuth2Options {
includeCredentialsOnRefreshOnBody?: boolean;
skipTokenRefresh?: boolean;
property?: string;
tokenType?: string;
keepBearer?: boolean;
tokenExpiredStatusCode?: number | number[];
/**
* Whether a `tokenExpiredStatusCode` other than 401 only forces a refresh when the stored
* token is at or past its expiry. Set this when the status is ambiguous, e.g. a gateway that
* answers 404 both for an expired token and for a resource that does not exist, so a batch of
* missing resources does not cost one refresh each. A 401 and an unknown expiry still refresh.
*/
skipRefreshWhileTokenIsFresh?: boolean;
keyToIncludeInAccessTokenHeader?: string;
}
export interface IConnection {
// The node the connection is to
node: string;
// The type of the input on destination node (for example "main")
type: NodeConnectionType;
// The output/input-index of destination node (if node has multiple inputs/outputs of the same type)
index: number;
}
export type ExecutionError =
| ExpressionError
| WorkflowActivationError
| WorkflowOperationError
| ExecutionCancelledError
| NodeOperationError
| NodeApiError;
export type ExecutionStorageLocation = 'db' | 'fs' | 's3' | 'az';
// Get used to gives nodes access to credentials
export interface IGetCredentials {
get(type: string, id: string | null): Promise<ICredentialsEncrypted>;
}
export abstract class ICredentials<T extends object = ICredentialDataDecryptedObject> {
id?: string;
name: string;
type: string;
data: string | undefined;
constructor(nodeCredentials: INodeCredentialsDetails, type: string, data?: string) {
this.id = nodeCredentials.id ?? undefined;
this.name = nodeCredentials.name;
this.type = type;
this.data = data;
}
abstract getData(nodeType?: string): Promise<T>;
abstract getDataToSave(): ICredentialsEncrypted;
abstract setData(data: T): Promise<void>;
abstract updateData(toUpdate: Partial<T>, toDelete?: Array<keyof T>): Promise<void>;
}
export interface IUser {
id: string;
email: string;
firstName: string;
lastName: string;
}
export type OAuth2FailureReason = 'invalid_token' | 'verifier_unavailable' | 'insufficient_scope';
export type N8nOAuth2ValidationResult =
| { valid: true; user: IUser }
| { valid: false; reason: OAuth2FailureReason };
export type N8nOAuth2FlowResult =
| {
valid: true;
token: string;
/** Rotated on every use. Never hand this to a browser document or to page script. */
refreshToken: string;
/** Lifetime of `token` in seconds, as the AS reports it. A duration, not an
* absolute `exp`, so a browser scheduling off it is immune to clock skew. */
expiresIn: number;
user: IUser;
metadata?: Record<string, string>;
}
| { valid: false; reason: string };
/**
* Result of trading a refresh token for a fresh pair. Carries no user: the grant
* the token belongs to already fixes the subject, and the caller re-validates the
* access token when it needs the identity.
*/
export type N8nOAuth2RefreshResult =
| { valid: true; token: string; refreshToken: string; expiresIn: number }
| { valid: false; reason: string };
export type ProjectSharingData = {
id: string;
name: string | null;
icon: { type: 'emoji' | 'icon'; value: string } | null;
type: 'personal' | 'team' | 'public';
createdAt: string;
updatedAt: string;
};
export interface ICredentialsDecrypted<T extends object = ICredentialDataDecryptedObject> {
id: string;
name: string;
type: string;
data?: T;
homeProject?: ProjectSharingData;
sharedWithProjects?: ProjectSharingData[];
isGlobal?: boolean;
isResolvable?: boolean;
usageScope?: 'project' | 'instance';
}
export interface ICredentialsEncrypted {
id?: string;
name: string;
type: string;
data?: string;
}
export interface ICredentialsExpressionResolveValues {
connectionInputData: INodeExecutionData[];
itemIndex: number;
node: INode;
runExecutionData: IRunExecutionData | null;
runIndex: number;
workflow: Workflow;
}
// Simplified options of request library
export interface IRequestOptionsSimplified {
auth?: {
username: string;
password: string;
sendImmediately?: boolean;
};
body: IDataObject;
headers: IDataObject;
qs: IDataObject;
}
export interface IRequestOptionsSimplifiedAuth {
auth?: {
username: string;
password: string;
sendImmediately?: boolean;
};
body?: IDataObject;
headers?: IDataObject;
qs?: IDataObject;
url?: string;
skipSslCertificateValidation?: boolean | string;
}
export interface IHttpRequestHelper {
helpers: { httpRequest: IAllExecuteFunctions['helpers']['httpRequest'] };
}
export interface IGetDecryptedCredentialsOptions {
credentialUsage?: 'trigger';
}
export abstract class ICredentialsHelper {
abstract getParentTypes(name: string): string[];
/**
* Returns false when the credential type sets `restrictToSupportedNodes: true`
* and `nodeType` is not in its `supportedNodes` list. Returns true otherwise
* — including when the credential type is unknown (caller will fail later).
*
* Used by both the execution-engine runtime guard and the workflow-save
* validator so the policy is defined in exactly one place.
*/
abstract isCredentialUsableByNode(credentialType: string, nodeType: string): boolean;
abstract authenticate(
credentials: ICredentialDataDecryptedObject,
typeName: string,
requestOptions: IHttpRequestOptions | IRequestOptionsSimplified,
workflow?: Workflow,
node?: INode,
): Promise<IHttpRequestOptions>;
abstract preAuthentication(
helpers: IHttpRequestHelper,
credentials: ICredentialDataDecryptedObject,
typeName: string,
node: INode,
credentialsExpired: boolean,
): Promise<ICredentialDataDecryptedObject | undefined>;
abstract runPreAuthentication(
helpers: IHttpRequestHelper,
credentials: ICredentialDataDecryptedObject,
typeName: string,
): Promise<ICredentialDataDecryptedObject | undefined>;
abstract getCredentials(
nodeCredentials: INodeCredentialsDetails,
type: string,
): Promise<ICredentials>;
abstract getDecrypted(
additionalData: IWorkflowExecuteAdditionalData,
nodeCredentials: INodeCredentialsDetails,
type: string,
mode: WorkflowExecuteMode,
executeData?: IExecuteData,
raw?: boolean,
expressionResolveValues?: ICredentialsExpressionResolveValues,
options?: IGetDecryptedCredentialsOptions,
): Promise<ICredentialDataDecryptedObject>;
abstract updateCredentials(
nodeCredentials: INodeCredentialsDetails,
type: string,
data: ICredentialDataDecryptedObject,
): Promise<void>;
abstract updateCredentialsOauthTokenData(
nodeCredentials: INodeCredentialsDetails,
type: string,
data: ICredentialDataDecryptedObject,
additionalData: IWorkflowExecuteAdditionalData,
): Promise<void>;
abstract getCredentialsProperties(type: string): INodeProperties[];
}
export interface IAuthenticateBase {
type: string;
properties:
| {
[key: string]: string;
}
| IRequestOptionsSimplifiedAuth;
}
export interface IAuthenticateGeneric extends IAuthenticateBase {
type: 'generic';
properties: IRequestOptionsSimplifiedAuth;
}
export type IAuthenticate =
| ((
credentials: ICredentialDataDecryptedObject,
requestOptions: IHttpRequestOptions,
) => Promise<IHttpRequestOptions>)
| IAuthenticateGeneric;
export interface IAuthenticateRuleBase {
type: string;
properties: {
[key: string]: string | number;
};
errorMessage?: string;
}
export interface IAuthenticateRuleResponseCode extends IAuthenticateRuleBase {
type: 'responseCode';
properties: {
value: number;
message: string;
};
}
export interface IAuthenticateRuleResponseSuccessBody extends IAuthenticateRuleBase {
type: 'responseSuccessBody';
properties: {
message: string;
key: string;
value: any;
};
}
type Override<A extends object, B extends object> = Omit<A, keyof B> & B;
export namespace DeclarativeRestApiSettings {
// The type below might be extended
// with new options that need to be parsed as expressions
export type HttpRequestOptions = Override<
IHttpRequestOptions,
{ skipSslCertificateValidation?: string | boolean; url?: string }
>;
export type ResultOptions = {
maxResults?: number | string;
options: HttpRequestOptions;
paginate?: boolean | string;
preSend: PreSendAction[];
postReceive: Array<{
data: {
parameterValue: string | IDataObject | undefined;
};
actions: PostReceiveAction[];
}>;
requestOperations?: IN8nRequestOperations;
};
}
export interface ICredentialTestRequest {
request: DeclarativeRestApiSettings.HttpRequestOptions;
rules?: IAuthenticateRuleResponseCode[] | IAuthenticateRuleResponseSuccessBody[];
}
export interface ICredentialTestRequestData {
nodeType?: INodeType;
testRequest: ICredentialTestRequest;
}
type ICredentialHttpRequestNode = {
name: string;
docsUrl: string;
hidden?: boolean;
} & ({ apiBaseUrl: string } | { apiBaseUrlPlaceholder: string });
export interface ICredentialType {
name: string;
displayName: string;
icon?: Icon;
iconColor?: ThemeIconColor;
iconUrl?: Themed<string>;
/**
* Base path for resolving file icons from expressions.
* Used when icon is an expression that resolves to `file:filename.svg`
* Format: `icons/${packageName}/${nodeDirPath}`
*/
iconBasePath?: string;
extends?: string[];
properties: INodeProperties[];
documentationUrl?: string;
__overwrittenProperties?: string[];
__skipManagedCreation?: boolean;
__showManagedOAuthScopes?: boolean;
authenticate?: IAuthenticate;
preAuthentication?: (
this: IHttpRequestHelper,
credentials: ICredentialDataDecryptedObject,
) => Promise<IDataObject>;
test?: ICredentialTestRequest;
genericAuth?: boolean;
httpRequestNode?: ICredentialHttpRequestNode;
supportedNodes?: string[];
/**
* If `true`, this credential can ONLY be used at runtime by nodes listed in
* `supportedNodes`. The execution engine will refuse to decrypt the credential
* for any other node — including the HTTP Request node and its tool variants,
* which normally have full access to all credential types.
*
* Opt-in. Existing credentials without this flag are unaffected.
*/
restrictToSupportedNodes?: true;
/**
* If `true`, the domain restriction fields will not be shown in the credential type properties.
*/
hideDomainRestrictionFields?: boolean;
/**
* If `true`, the credential type will not be shown in the credentials add modal
*/
hidden?: boolean;
}
export interface ICredentialTypes {
recognizes(credentialType: string): boolean;
getByName(credentialType: string): ICredentialType;
getSupportedNodes(type: string): string[];
getParentTypes(typeName: string): string[];
}
// The way the credentials get saved in the database (data encrypted)
export interface ICredentialData {
id?: string;
name: string;
data: string; // Contains the access data as encrypted JSON string
}
// The encrypted credentials which the nodes can access
export type CredentialInformation =
| string
| string[]
| number
| boolean
| IDataObject
| IDataObject[];
// The encrypted credentials which the nodes can access
export interface ICredentialDataDecryptedObject {
[key: string]: CredentialInformation;
}
// First array index: The output/input-index (if node has multiple inputs/outputs of the same type)
// Second array index: The different connections (if one node is connected to multiple nodes)
// Any index can be null, for example in a switch node with multiple indexes some of which are not connected
export type NodeInputConnections = Array<IConnection[] | null>;
export interface INodeConnection {
sourceIndex: number;
destinationIndex: number;
}
export interface INodeConnections {
// Input name
[key: string]: NodeInputConnections;
}
export interface IConnections {
// Node name
[key: string]: INodeConnections;
}
export type GenericValue = string | object | number | boolean | undefined | null;
export type CloseFunction = () => Promise<void>;
export interface IDataObject {
[key: string]: GenericValue | IDataObject | GenericValue[] | IDataObject[];
}
export type IExecuteResponsePromiseData = IDataObject | IN8nHttpFullResponse;
export interface INodeTypeNameVersion {
name: string;
version: number;
}
export interface IRunNodeResponse {
data: INodeExecutionData[][] | null | undefined;
hints?: NodeExecutionHint[];
closeFunction?: CloseFunction;
}
export interface ISourceDataConnections {
// Key for each input type and because there can be multiple inputs of the same type it is an array
// null is also allowed because if we still need data for a later while executing the workflow set temporary to null
// the nodes get as input TaskDataConnections which is identical to this one except that no null is allowed.
[key: string]: Array<ISourceData[] | null>;
}
export interface IExecuteData {
data: ITaskDataConnections;
metadata?: ITaskMetadata;
node: INode;
source: ITaskDataConnectionsSource | null;
runIndex?: number;
}
export type IContextObject = {
[key: string]: any;
};
export interface IExecuteContextData {
// Keys are: "flow" | "node:<NODE_NAME>"
[key: string]: IContextObject;
}
export type IHttpRequestMethods =
| 'DELETE'
| 'GET'
| 'PATCH'
| 'POST'
| 'PUT'
| 'PROPFIND'
| 'MKCOL'
| 'MOVE'
| 'COPY'
| 'REPORT'
| 'HEAD';
export type IgnoreStatusErrorConfig = {
ignore: true;
/** Ignore HTTP status errors except for the specified status codes */
except: number[];
};
/** used in helpers.httpRequest(WithAuthentication) */
export interface IHttpRequestOptions {
url: string;
baseURL?: string;
headers?: IDataObject;
method?: IHttpRequestMethods;
body?: FormData | GenericValue | GenericValue[] | Buffer | URLSearchParams;
qs?: IDataObject;
arrayFormat?: 'indices' | 'brackets' | 'repeat' | 'comma';
auth?: {
username: string;
password: string;
sendImmediately?: boolean;
};
disableFollowRedirect?: boolean;
maxRedirects?: number; // Ignored when `disableFollowRedirect` is `true`.
encoding?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream';
skipSslCertificateValidation?: boolean;
returnFullResponse?: boolean;
ignoreHttpStatusErrors?: boolean | IgnoreStatusErrorConfig;
proxy?: {
host: string;
port: number;
auth?: {
username: string;
password: string;
};
protocol?: string;
};
timeout?: number;
json?: boolean;
abortSignal?: GenericAbortSignal;
/**
* Whether to send credentials on cross-origin redirects
* @default true - for backwards compatibility
*/
sendCredentialsOnCrossOriginRedirect?: boolean;
/**
* Comma-separated list of allowed domains for credential requests.
* If set, requests to domains not in this list will be blocked.
*/
allowedDomains?: string;
agentOptions?: Omit<AgentOptions, 'socket' | 'lookup'>;
}
/**
* used in helpers.request(WithAuthentication)
* @see IHttpRequestOptions
* @deprecated Prefer using IHttpRequestOptions
*/
export interface IRequestOptions {
baseURL?: string;
uri?: string;
url?: string;
method?: IHttpRequestMethods;
qs?: IDataObject;
qsStringifyOptions?: { arrayFormat: 'repeat' | 'brackets' | 'indices' };
useQuerystring?: boolean;
headers?: IDataObject;
auth?: Partial<{
sendImmediately: boolean;
bearer: string;
user: string;
username: string;
password: string;
pass: string;
}>;
body?: any;
formData?: IDataObject | FormData;
form?: IDataObject | FormData;
json?: boolean;
useStream?: boolean;
encoding?: string | null;
timeout?: number;
rejectUnauthorized?: boolean;
proxy?: string | AxiosProxyConfig;
simple?: boolean;
gzip?: boolean;
resolveWithFullResponse?: boolean;
/** Whether to follow GET or HEAD HTTP 3xx redirects @default true */
followRedirect?: boolean;
/** Whether to follow **All** HTTP 3xx redirects @default false */
followAllRedirects?: boolean;
/** Max number of redirects to follow @default 21 */
maxRedirects?: number;
agentOptions?: SecureContextOptions;
/**
* Whether to send credentials on cross-origin redirects
* @default true - for backwards compatibility
*/
sendCredentialsOnCrossOriginRedirect?: boolean;
/**
* Comma-separated list of allowed domains for credential requests.
* If set, requests to domains not in this list will be blocked.
*/
allowedDomains?: string;
}
export interface PaginationOptions {
binaryResult?: boolean;
continue: boolean | string;
request: IRequestOptionsSimplifiedAuth;
requestInterval: number;
maxRequests?: number;
}
export type IN8nHttpResponse = IDataObject | Buffer | GenericValue | GenericValue[] | null;
export interface IN8nHttpFullResponse {
body: IN8nHttpResponse | Readable;
__bodyResolved?: boolean;
headers: IDataObject;
statusCode: number;
statusMessage?: string;
}
export interface IN8nRequestOperations {
pagination?:
| IN8nRequestOperationPaginationGeneric
| IN8nRequestOperationPaginationOffset
| ((
this: IExecutePaginationFunctions,
requestOptions: DeclarativeRestApiSettings.ResultOptions,
) => Promise<INodeExecutionData[]>);
}
export interface IN8nRequestOperationPaginationBase {
type: string;
properties: {
[key: string]: unknown;
};
}
export interface IN8nRequestOperationPaginationGeneric extends IN8nRequestOperationPaginationBase {
type: 'generic';
properties: {
continue: boolean | string;
request: IRequestOptionsSimplifiedAuth;
};
}
export interface IN8nRequestOperationPaginationOffset extends IN8nRequestOperationPaginationBase {
type: 'offset';
properties: {
limitParameter: string;
offsetParameter: string;
pageSize: number;
rootProperty?: string; // Optional Path to option array
type: 'body' | 'query';
};
}
export type EnsureTypeOptions = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'json';
export interface IGetNodeParameterOptions {
contextNode?: INode;
// make sure that returned value would be of specified type, converts it if needed
ensureType?: EnsureTypeOptions;
// extract value from regex, works only when parameter type is resourceLocator
extractValue?: boolean;
// get raw value of parameter with unresolved expressions
rawExpressions?: boolean;
// skip validation of parameter
skipValidation?: boolean;
}
namespace ExecuteFunctions {
namespace StringReturning {
export type NodeParameter =
| 'binaryProperty'
| 'binaryPropertyName'
| 'binaryPropertyOutput'
| 'dataPropertyName'
| 'dataBinaryProperty'
| 'resource'
| 'operation'
| 'filePath'
| 'encodingType';
}
namespace NumberReturning {
export type NodeParameter = 'limit';
}
namespace BooleanReturning {
export type NodeParameter =
| 'binaryData'
| 'download'
| 'jsonParameters'
| 'returnAll'
| 'rawData'
| 'resolveData';
}
namespace RecordReturning {
export type NodeParameter = 'additionalFields' | 'filters' | 'options' | 'updateFields';
}
export type GetNodeParameterFn = {
// @TECH_DEBT: Refactor to remove this barely used overload - N8N-5632
getNodeParameter<T extends { resource: string }>(
parameterName: 'resource',
itemIndex?: number,
): T['resource'];
getNodeParameter(
parameterName: StringReturning.NodeParameter,
itemIndex: number,
fallbackValue?: string,
options?: IGetNodeParameterOptions,
): string;
getNodeParameter(
parameterName: RecordReturning.NodeParameter,
itemIndex: number,
fallbackValue?: IDataObject,
options?: IGetNodeParameterOptions,
): IDataObject;
getNodeParameter(
parameterName: BooleanReturning.NodeParameter,
itemIndex: number,
fallbackValue?: boolean,
options?: IGetNodeParameterOptions,
): boolean;
getNodeParameter(
parameterName: NumberReturning.NodeParameter,
itemIndex: number,
fallbackValue?: number,
options?: IGetNodeParameterOptions,
): number;
getNodeParameter(
parameterName: string,
itemIndex: number,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
};
}
export interface IExecuteWorkflowInfo {
code?: IWorkflowBase;
id?: string;
}
export type ICredentialTestFunction = (
this: ICredentialTestFunctions,
credential: ICredentialsDecrypted<ICredentialDataDecryptedObject>,
) => Promise<INodeCredentialTestResult>;
export interface ICredentialTestFunctions {
logger: Logger;
helpers: SSHTunnelFunctions & {
request: (uriOrObject: string | object, options?: object) => Promise<any>;
};
}
export interface BaseHelperFunctions {
createDeferredPromise: <T = void>() => IDeferredPromise<T>;
returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[];
}
const __brand = Symbol('resolvedFilePath');
export type ResolvedFilePath = string & {
[__brand]: 'ResolvedFilePath';
};
/**
* A directory pinned by an open file descriptor. Resolving names through it
* addresses entries relative to the held descriptor rather than re-walking the
* path. Always {@link PinnedDirectory.close} it when done.
*/
export interface PinnedDirectory {
/** A path that resolves `name` relative to the pinned directory descriptor. */
resolvePath(name: string): string;
close(): Promise<void>;
}
export interface FileSystemHelperFunctions {
resolvePath(path: PathLike): Promise<ResolvedFilePath>;
/**
* Use {@link resolvePath} to resolve the path first.
*/
isFilePathBlocked(filePath: ResolvedFilePath): boolean;
/**
* Throws if any directory component of the path is a symlink. Use {@link resolvePath} first.
*/
assertNoSymlinkInPath(filePath: ResolvedFilePath): Promise<void>;
/**
* Creates the parent directory of the path and any missing ancestors without following symlinks.
* Use {@link resolvePath} first.
*/
ensureParentDirectoryWithoutFollowingSymlinks(filePath: ResolvedFilePath): Promise<void>;
/**
* Resolves a directory that is safe to use as a staging location for the given
* target: the realpath'd allowed base that contains the target, with a fallback
* for when no path restriction is configured. Use {@link resolvePath} first.
*/
resolveStagingBaseForTarget(filePath: ResolvedFilePath): Promise<ResolvedFilePath>;
/**
* Pins the directory at `directoryPath` by descending from a trusted base one
* component at a time without following symlinks, returning a handle whose
* descriptor the final operation can address entries relative to. Returns
* `null` when descriptor-relative resolution is unavailable (non-Linux) or the
* directory is not within a trusted base, in which case the caller should fall
* back to {@link assertNoSymlinkInPath}.
*/
pinDirectory(
directoryPath: string,
options: { create: boolean },
): Promise<PinnedDirectory | null>;
/**
* Use {@link resolvePath} to resolve the path first.
*/
createReadStream(filePath: ResolvedFilePath): Promise<Readable>;
getStoragePath(): string;
/**
* Use {@link resolvePath} to resolve the path first.
*/
writeContentToFile(
path: ResolvedFilePath,
content: string | Buffer | Readable,
flag?: number,
): Promise<void>;
}
export interface BinaryHelperFunctions {
prepareBinaryData(
binaryData: Buffer | Readable,
filePath?: string,
mimeType?: string,
): Promise<IBinaryData>;
setBinaryDataBuffer(data: IBinaryData, binaryData: Buffer): Promise<IBinaryData>;
/** @deprecated */
copyBinaryFile(): Promise<never>;
binaryToBuffer(body: Buffer | Readable): Promise<Buffer>;
binaryToString(body: Buffer | Readable, encoding?: BufferEncoding): Promise<string>;
getBinaryPath(binaryDataId: string): string;
getBinaryStream(binaryDataId: string, chunkSize?: number): Promise<Readable>;
createBinarySignedUrl(binaryData: IBinaryData, expiresIn?: string): string;
getBinaryMetadata(binaryDataId: string): Promise<{
fileName?: string;
mimeType?: string;
fileSize: number;
}>;
}
export type DeduplicationScope = 'node' | 'workflow';
export type DeduplicationItemTypes = string | number;
export type DeduplicationMode = 'entries' | 'latestIncrementalKey' | 'latestDate';
export interface IProcessedDataLatest {
mode: DeduplicationMode;
data: DeduplicationItemTypes;
}
export interface IProcessedDataEntries {
mode: DeduplicationMode;
data: DeduplicationItemTypes[];
}
export interface IDeduplicationOutput {
new: DeduplicationItemTypes[];
processed: DeduplicationItemTypes[];
}
export interface IDeduplicationOutputItems {
new: IDataObject[];
processed: IDataObject[];
}
export interface ICheckProcessedOptions {
mode: DeduplicationMode;
maxEntries?: number;
}
export interface DeduplicationHelperFunctions {
checkProcessedAndRecord(
items: DeduplicationItemTypes[],
scope: DeduplicationScope,
options: ICheckProcessedOptions,
): Promise<IDeduplicationOutput>;
checkProcessedItemsAndRecord(
propertyName: string,
items: IDataObject[],
scope: DeduplicationScope,
options: ICheckProcessedOptions,
): Promise<IDeduplicationOutputItems>;
removeProcessed(
items: DeduplicationItemTypes[],
scope: DeduplicationScope,
options: ICheckProcessedOptions,
): Promise<void>;
clearAllProcessedItems(scope: DeduplicationScope, options: ICheckProcessedOptions): Promise<void>;
getProcessedDataCount(
scope: DeduplicationScope,
options: ICheckProcessedOptions,
): Promise<number>;
}
interface NodeHelperFunctions {
copyBinaryFile(filePath: string, fileName: string, mimeType?: string): Promise<IBinaryData>;
}
/**
* Controls whether an outbound HTTP client is subject to the instance's
* outbound network policy (SSRF protection).
*
* - `'safe'` (default): the client enforces the instance policy. Whether the
* guard actually runs is decided inside `OutboundHttp` from
* `SsrfProtectionConfig.enabled` — callers never read that flag themselves.
* - `'enforced'`: the guard runs unconditionally, regardless of
* `SsrfProtectionConfig.enabled`. Reserve this for destinations that must
* stay guarded even on instances that leave protection off.
* - `'unsafe'`: the client bypasses the policy unconditionally. Reserve this
* for fixed, n8n-owned or operator-configured destinations, and state the
* reason in a comment at the call site.
*/
export type UseDefaultSsrfPolicy = 'safe' | 'enforced' | 'unsafe';
/**
* Egress filter exposed to nodes whose embedded HTTP clients cannot go through
* `httpRequest`. Mirrors the layers n8n's own egress uses: a pre-flight URL
* validation, a connect-time secure DNS lookup, and per-redirect validation.
*/
export interface NodeEgressFilter {
/** Validate a target URL before any connection. Resolves hostnames; direct IP literals are checked without DNS. */
validateUrl(url: string | URL): Promise<Result<void, Error>>;
/** Validate a connection host no DNS lookup will see (e.g. an IP-literal proxy host), without resolving it. */
validateConnectionHost(host: string): Result<void, Error>;
/** DNS lookup drop-in that validates resolved addresses against the configured egress rules. */
createSecureLookup(): LookupFunction;
/** Validate a redirect hop synchronously; throws when the target is not allowed. */
validateRedirectSync(url: string): void;
}
export interface RequestHelperFunctions {
httpRequest(requestOptions: IHttpRequestOptions): Promise<any>;
httpRequestWithAuthentication(
this: IAllExecuteFunctions,
credentialsType: string,
requestOptions: IHttpRequestOptions,
additionalCredentialOptions?: IAdditionalCredentialOptions,
): Promise<any>;
requestWithAuthenticationPaginated(
this: IAllExecuteFunctions,
requestOptions: IRequestOptions,
itemIndex: number,
paginationOptions: PaginationOptions,
credentialsType?: string,
additionalCredentialOptions?: IAdditionalCredentialOptions,
sanitizedRequest?: IDataObject,
): Promise<any[]>;
/**
* @deprecated Use .httpRequest instead
* @see RequestHelperFunctions.httpRequest
*/
request(uriOrObject: string | IRequestOptions, options?: IRequestOptions): Promise<any>;
/**
* @deprecated Use .httpRequestWithAuthentication instead
* @see RequestHelperFunctions.requestWithAuthentication
*/
requestWithAuthentication(
this: IAllExecuteFunctions,
credentialsType: string,
requestOptions: IRequestOptions,
additionalCredentialOptions?: IAdditionalCredentialOptions,
itemIndex?: number,
): Promise<any>;
/**
* @deprecated Use .httpRequestWithAuthentication instead
* @see RequestHelperFunctions.requestWithAuthentication
*/
requestOAuth1(
this: IAllExecuteFunctions,
credentialsType: string,
requestOptions: IRequestOptions,
): Promise<any>;
/**
* @deprecated Use .httpRequestWithAuthentication instead
* @see RequestHelperFunctions.requestWithAuthentication
*/
requestOAuth2(
this: IAllExecuteFunctions,
credentialsType: string,
requestOptions: IRequestOptions,
oAuth2Options?: IOAuth2Options,
): Promise<any>;
refreshOAuth2Token(
this: IAllExecuteFunctions,
credentialsType: string,
oAuth2Options?: IOAuth2Options,
): Promise<any>;
/**
* Returns the instance egress filter for clients that build their own HTTP
* transport. Under the default `'safe'` policy, this is a passthrough
* implementation when egress filtering is not configured, so callers can
* always use the returned filter unguarded.
* @param {UseDefaultSsrfPolicy} [useDefaultSsrfPolicy='safe'] how the instance policy applies to the returned filter
*/
getSecureEgressFilter(useDefaultSsrfPolicy?: UseDefaultSsrfPolicy): NodeEgressFilter;
}
export type SSHCredentials = {
sshHost: string;
sshPort: number;
sshUser: string;
} & (
| {
sshAuthenticateWith: 'password';
sshPassword: string;
}
| {
sshAuthenticateWith: 'privateKey';
// TODO: rename this to `sshPrivateKey`
privateKey: string;
// TODO: rename this to `sshPassphrase`
passphrase?: string;
}
);
export interface SSHTunnelFunctions {
getSSHClient(credentials: SSHCredentials, abortController?: AbortController): Promise<SSHClient>;
updateLastUsed(client: SSHClient): void;
}
type CronUnit = number | '*' | `*/${number}`;
export type CronExpression =
`${CronUnit} ${CronUnit} ${CronUnit} ${CronUnit} ${CronUnit} ${CronUnit}`;
type CronRecurrenceRule =
| { activated: false }
| {
activated: true;
index: number;
intervalSize: number;
typeInterval: 'minutes' | 'hours' | 'days' | 'weeks' | 'months';
};
/**
* @deprecated Remnant of the legacy in-memory scheduling path. `registerCron`
* takes {@link Cron}, not this type; the durable scheduler path never uses it.
* Only `ScheduledTaskManager` and its helper still reference it (and only for
* `CronContext['recurrence']`). Slated to go away with `ScheduledTaskManager`.
*/
export type CronContext = {
nodeId: string;
workflowId: string;
timezone: string;
expression: CronExpression;
recurrence?: CronRecurrenceRule;
};
/**
* Optional source descriptor.
* Which Schedule Trigger field produced this cron rule.
*
* Two different fields can produce the same cron string: a rule typed as a raw
* cron expression and a rule set to "every 30 seconds" can both come out as the
* same text. Knowing the original field lets code downstream tell them apart
* and store each one under the right kind.
*/
export type CronSource = {
field: 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months' | 'cronExpression';
/** The interval size N (e.g. every N seconds); absent for a raw cron expression. */
size?: number;
};
export type Cron = {
expression: CronExpression;
recurrence?: CronRecurrenceRule;
source?: CronSource;
};
export interface SchedulingFunctions {
registerCron(cron: Cron, onTick: (scheduledT: Date) => void): void;
}
export type NodeTypeAndVersion = {
name: string;
type: string;
typeVersion: number;
disabled: boolean;
parameters?: INodeParameters;
};
export interface FunctionsBase {
logger: Logger;
customData: IWorkflowExecutionCustomData;
getCredentials<T extends object = ICredentialDataDecryptedObject>(
type: string,
itemIndex?: number,
): Promise<T>;
getCredentialsProperties(type: string): INodeProperties[];
getExecutionId(): string;
getNode(): INode;
getWorkflow(): IWorkflowMetadata;
getWorkflowSettings(): IWorkflowSettings;
getWorkflowStaticData(type: string): IDataObject;
getTimezone(): string;
getRestApiUrl(): string;
getInstanceBaseUrl(): string;
getInstanceId(): string;
/** Get the waiting resume url signed with the signature token */
getSignedResumeUrl(parameters?: Record<string, string>): string;
getChildNodes(
nodeName: string,
options?: { includeNodeParameters?: boolean },
): NodeTypeAndVersion[];
getParentNodes(
nodeName: string,
options?: {
includeNodeParameters?: boolean;
connectionType?: NodeConnectionType;
depth?: number;
},
): NodeTypeAndVersion[];
getKnownNodeTypes(): IDataObject;
getMode?: () => WorkflowExecuteMode;
getActivationMode?: () => WorkflowActivateMode;
getChatTrigger: () => INode | null;
isNodeFeatureEnabled(featureName: string): boolean;
getExecutionContext: () => IExecutionContext | undefined;
listAgents?(): Promise<Array<{ id: string; name: string }>>;
/** @deprecated */
prepareOutputData(outputData: INodeExecutionData[]): Promise<INodeExecutionData[][]>;
}
type FunctionsBaseWithRequiredKeys<Keys extends keyof FunctionsBase> = FunctionsBase & {
[K in Keys]: NonNullable<FunctionsBase[K]>;
};
export type ContextType = 'flow' | 'node';
export type DataTableProxyProvider = {
getDataTableAggregateProxy(
workflow: Workflow,
node: INode,
projectId?: string,
): Promise<IDataTableProjectAggregateService>;
getDataTableProxy(
workflow: Workflow,
node: INode,
dataTableId: string,
projectId?: string,
): Promise<IDataTableProjectService>;
};
export type DataTableProxyFunctions = {
// These are optional to account for situations where the data-table module is disabled
getDataTableAggregateProxy?(): Promise<IDataTableProjectAggregateService>;
getDataTableProxy?(dataTableId: string): Promise<IDataTableProjectService>;
};
export type CredentialCheckStatus = {
credentialId: string;
credentialName: string;
credentialType: string;
resolverId?: string;
status: 'missing' | 'configured' | 'resolver_missing';
authorizationUrl?: string;
revokeUrl?: string;
/**
* Absolute URL of the credential type's provider icon. Resolved server-side
* because consumers (e.g. the form hosting shell, rendered from nodes-base)
* have no access to the credential registry.
*/
iconUrl?: string;
};
export type CredentialCheckResult = {
readyToExecute: boolean;
credentials: CredentialCheckStatus[];
};
/**
* The authoritative root-workflow nodes to check, taken from the SAME workflow snapshot
* that is executing — the published version on a live webhook, the execution snapshot on a
* waiting form, the draft on a test webhook. Passing the node objects (rather than a
* persisted workflow id alone) fixes two things at once: it restricts the check to the
* nodes that can actually run on this trigger (disjoint branches and other triggers' chains
* are simply not in the list), AND it pins the check to the running snapshot, so a node
* renamed or re-wired in a draft that differs from the running version can't make the
* resolver silently skip a credential.
*
* When omitted, every enabled node of the persisted workflow is checked (the safe default,
* used by callers that only have a workflow id — e.g. the form connect panel).
*/
export type CredentialCheckOptions = {
rootNodes?: INode[];
};
export type DynamicCredentialCheckProxyProvider = {
checkCredentialStatus(
workflowId: string,
executionContext: {
credentials?: string;
},
options?: CredentialCheckOptions,
): Promise<CredentialCheckResult>;
};
export type CredentialCheckProxyFunctions = {
// Optional to account for situations where the dynamic-credentials module is disabled
checkCredentialStatus?(
workflowId: string,
executionContext: {
credentials?: string;
},
options?: CredentialCheckOptions,
): Promise<CredentialCheckResult>;
};
export type OauthJweProxyProvider = {
decryptOAuth2TokenData(tokenData: IDataObject): Promise<IDataObject>;
};
type BaseExecutionFunctions = FunctionsBaseWithRequiredKeys<'getMode'> & {
continueOnFail(): boolean;
setMetadata(metadata: ITaskMetadata): void;
evaluateExpression(expression: string, itemIndex: number): NodeParameterValueType;
getContext(type: ContextType): IContextObject;
getExecuteData(): IExecuteData;
getWorkflowDataProxy(itemIndex: number): IWorkflowDataProxyData;
getInputSourceData(inputIndex?: number, connectionType?: NodeConnectionType): ISourceData;
getExecutionCancelSignal(): AbortSignal | undefined;
onExecutionCancellation(handler: () => unknown): void;
/**
* Registers a handler run when the execution ends (success, failure, or
* cancellation) — not when it pauses into the waiting state. May fire more
* than once, so handlers must be idempotent; errors they throw are caught
* and logged. Unavailable in contexts without lifecycle hooks, so callers
* need their own fallback cleanup.
*/
onExecutionFinish?(handler: () => unknown): void;
logAiEvent(eventName: AiEvent, msg?: string): void;
};
// TODO: Create later own type only for Config-Nodes
export type IExecuteFunctions = ExecuteFunctions.GetNodeParameterFn &
BaseExecutionFunctions & {
executeWorkflow(
workflowInfo: IExecuteWorkflowInfo,
inputData?: INodeExecutionData[],
parentCallbackManager?: CallbackManager,
options?: {
doNotWaitToFinish?: boolean;
parentExecution?: RelatedExecution;
executionMode?: WorkflowExecuteMode;
returnLastRunOnly?: boolean; // Forces the caller to receive only the items from the terminal node's final run.
},
): Promise<ExecuteWorkflowData>;
executeAgent(
agentInfo: ExecuteAgentInfo,
message: string,
executionId: string,
itemIndex: number,
): Promise<ExecuteAgentData>;
getExecutionDataById(executionId: string): Promise<IRunExecutionData | undefined>;
getInputConnectionData(
connectionType: AINodeConnectionType,
itemIndex: number,
inputIndex?: number,
): Promise<unknown>;
getInputData(inputIndex?: number, connectionType?: NodeConnectionType): INodeExecutionData[];
getNodeInputs(): INodeInputConfiguration[];
getNodeOutputs(): INodeOutputConfiguration[];
getRuntimeCredential(alias: string): Promise<IDataObject[string] | undefined>;
/**
* Pauses the execution until `waitTill`.
*
* Set `acceptsResumeRequest` to `false` when only the deadline can end the wait.
* The engine then keeps a short wait in the process, so the wait does not survive
* a restart. Without the option the engine suspends and persists the execution.
*/
putExecutionToWait(waitTill: Date, options?: { acceptsResumeRequest?: boolean }): Promise<void>;
sendMessageToUI(message: any): void;
/** Whether the run's resolved redaction policy redacts console output for this execution's mode */
isConsoleOutputRedacted(): boolean;
sendResponse(response: IExecuteResponsePromiseData): Promise<void>;
sendChunk(type: ChunkType, itemIndex: number, content?: IDataObject | string): void;
isStreaming(): boolean;
/** Returns true if the node is being executed as an AI Agent tool */
isToolExecution(): boolean;
// TODO: Make this one then only available in the new config one
addInputData(
connectionType: NodeConnectionType,
data: INodeExecutionData[][] | ExecutionError,
runIndex?: number,
): { index: number };
addOutputData(
connectionType: NodeConnectionType,
currentNodeRunIndex: number,
data: INodeExecutionData[][] | ExecutionError,
metadata?: ITaskMetadata,
sourceNodeRunIndex?: number,
): void;
addExecutionHints(...hints: NodeExecutionHint[]): void;
nodeHelpers: NodeHelperFunctions;
helpers: RequestHelperFunctions &
BaseHelperFunctions &
BinaryHelperFunctions &
DeduplicationHelperFunctions &
FileSystemHelperFunctions &
SSHTunnelFunctions &
DataTableProxyFunctions &
CredentialCheckProxyFunctions & {
normalizeItems(items: INodeExecutionData | INodeExecutionData[]): INodeExecutionData[];
constructExecutionMetaData(
inputData: INodeExecutionData[],
options: { itemData: IPairedItemData | IPairedItemData[] },
): NodeExecutionWithMetadata[];
assertBinaryData(itemIndex: number, parameterData: string | IBinaryData): IBinaryData;
getBinaryDataBuffer(
itemIndex: number,
parameterData: string | IBinaryData,
): Promise<Buffer>;
detectBinaryEncoding(buffer: Buffer): string;
copyInputItems(items: INodeExecutionData[], properties: string[]): IDataObject[];
};
getParentCallbackManager(): CallbackManager | undefined;
startJob<T = unknown, E = unknown>(
jobType: string,
settings: unknown,
itemIndex: number,
): Promise<Result<T, E>>;
getRunnerStatus(taskType: string): { available: true } | { available: false; reason?: string };
};
export interface IExecuteSingleFunctions extends BaseExecutionFunctions {
getInputData(inputIndex?: number, connectionType?: NodeConnectionType): INodeExecutionData;
getItemIndex(): number;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getRuntimeCredential(alias: string): Promise<IDataObject[string] | undefined>;
helpers: RequestHelperFunctions &
BaseHelperFunctions &
BinaryHelperFunctions & {
assertBinaryData(propertyName: string, inputIndex?: number): IBinaryData;
getBinaryDataBuffer(propertyName: string, inputIndex?: number): Promise<Buffer>;
detectBinaryEncoding(buffer: Buffer): string;
};
}
export type ISupplyDataFunctions = ExecuteFunctions.GetNodeParameterFn &
FunctionsBaseWithRequiredKeys<'getMode'> &
Pick<
IExecuteFunctions,
| 'addInputData'
| 'addOutputData'
| 'getInputConnectionData'
| 'getInputData'
| 'getNodeOutputs'
| 'executeWorkflow'
| 'sendMessageToUI'
| 'isConsoleOutputRedacted'
| 'startJob'
| 'helpers'
| 'isToolExecution'
> & {
getNextRunIndex(): number;
continueOnFail(): boolean;
evaluateExpression(expression: string, itemIndex: number): NodeParameterValueType;
getWorkflowDataProxy(itemIndex: number): IWorkflowDataProxyData;
getExecutionCancelSignal(): AbortSignal | undefined;
onExecutionCancellation(handler: () => unknown): void;
/** See {@link BaseExecutionFunctions.onExecutionFinish} */
onExecutionFinish?(handler: () => unknown): void;
logAiEvent(eventName: AiEvent, msg?: string): void;
addExecutionHints(...hints: NodeExecutionHint[]): void;
cloneWith(replacements: {
runIndex: number;
inputData: INodeExecutionData[][];
}): ISupplyDataFunctions;
};
export interface IExecutePaginationFunctions extends IExecuteSingleFunctions {
makeRoutingRequest(
this: IAllExecuteFunctions,
requestOptions: DeclarativeRestApiSettings.ResultOptions,
): Promise<INodeExecutionData[]>;
}
export interface ILoadOptionsFunctions extends FunctionsBase {
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
getCurrentNodeParameter(
parameterName: string,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object | undefined;
getCurrentNodeParameters(): INodeParameters | undefined;
helpers: RequestHelperFunctions & SSHTunnelFunctions & DataTableProxyFunctions;
}
export type FieldValueOption = { name: string; type: FieldType | 'any' };
export type IWorkflowNodeContext = ExecuteFunctions.GetNodeParameterFn &
Pick<FunctionsBase, 'getNode' | 'getWorkflow'>;
export interface ILocalLoadOptionsFunctions {
getWorkflowNodeContext(
nodeType: string,
useActiveVersion?: boolean,
): Promise<IWorkflowNodeContext | null>;
}
export interface IWorkflowLoader {
get(workflowId: string): Promise<IWorkflowBase>;
}
/** A poll node's opaque static-data-shaped state, durably stored between polls. */
export type PollCursor = IDataObject;
export interface IPollFunctions
extends FunctionsBaseWithRequiredKeys<'getMode' | 'getActivationMode'> {
__emit(
data: INodeExecutionData[][],
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
donePromise?: IDeferredPromise<IRun | undefined>,
): void;
/**
* Milliseconds this poll may spend before the engine abandons the tick.
* A node draining a large backlog should stop fetching before the budget
* runs out, return what it has, and leave the rest to the next poll.
*/
getPollBudgetMs(): number;
__emitError(error: Error, responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>): void;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
/** Persists a cursor staged by a poll that emitted no items. Called by the tick engines, never by a node. */
__commitCursor?(): Promise<void>;
/**
* Runs a poll and the hand-off that follows it in one staging scope, so a cursor
* can only be committed by the poll that staged it. Called by the tick engines,
* never by a node.
*/
__runPoll?<T>(poll: () => Promise<T>): Promise<T>;
helpers: RequestHelperFunctions &
BaseHelperFunctions &
BinaryHelperFunctions &
SchedulingFunctions;
}
export interface ITriggerFunctions
extends FunctionsBaseWithRequiredKeys<'getMode' | 'getActivationMode'> {
emit(
data: INodeExecutionData[][],
responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>,
donePromise?: IDeferredPromise<IRun>,
deduplicationKey?: string,
): void;
/**
* Persist the current run as a failed execution and run the error workflow if configured.
* Use this when the trigger or poller encounters an error during a run (e.g. processing failed)
* but the listener/connection is still healthy. The workflow stays active.
*
* @param error - The execution error to persist and pass to the error workflow
*/
saveFailedExecution(error: ExecutionError): void;
/**
* Report a fatal trigger/poller error: deactivate the workflow, run the error workflow if configured,
* and queue the workflow for reactivation with backoff. Use this when the trigger or poller cannot
* continue (e.g. connection lost, auth failure). The workflow is removed from active workflows
* and will be retried periodically until it activates successfully.
*
* @param error - The error that caused the trigger or poller to fail
* @param responsePromise - Optional. When provided (e.g. in manual trigger mode), used to signal the error to the caller
*/
emitError(error: Error, responsePromise?: IDeferredPromise<IExecuteResponsePromiseData>): void;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
helpers: RequestHelperFunctions &
BaseHelperFunctions &
BinaryHelperFunctions &
SSHTunnelFunctions &
SchedulingFunctions;
}
export interface IHookFunctions
extends FunctionsBaseWithRequiredKeys<'getMode' | 'getActivationMode'> {
getWebhookName(): string;
getWebhookDescription(name: WebhookType): IWebhookDescription | undefined;
getNodeWebhookUrl: (name: WebhookType) => string | undefined;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
helpers: RequestHelperFunctions;
}
export interface IWebhookFunctions extends FunctionsBaseWithRequiredKeys<'getMode'> {
getBodyData(): IDataObject;
getHeaderData(): IncomingHttpHeaders;
/**
* Identity pipeline for identity-bearing triggers (Form, MCP).
*
* These let a trigger run a workflow as the *caller* — resolving that user's own
* private (per-user) credentials instead of a shared/static credential — by
* proving the caller's n8n identity against the internal Authorization Server (AS)
* and binding it to the execution. The shape is acquire → verify → bind:
*
* - Browser-facing triggers (Form) acquire a token interactively:
* `beginN8nOAuth2Flow` → (AS redirect) → `completeN8nOAuth2Flow`.
* - Resource-server triggers (MCP) receive a bearer token directly and only
* `validateN8nOAuth2Token` it.
* - Either way, `establishTriggerIdentity` binds the verified token to the run.
*/
/**
* Starts the interactive authorization-code + PKCE flow against n8n's internal AS
* for `resourceUrl` (the trigger's own protected-resource URL). Returns the
* `/oauth/authorize` URL to redirect the browser to; the AS identifies the
* already-logged-in user from their n8n session (no login prompt for the
* first-party trigger client) and redirects back to the trigger URL with a code.
* Used on the initial GET of a browser-facing trigger. Pair with
* `completeN8nOAuth2Flow`.
*
* Optional `metadata` is stashed server-side against this flow's one-time `state`
* (never sent to the browser) and returned by `completeN8nOAuth2Flow` on success —
* a per-flow slot for carrying data (e.g. the original request query) across the bounce.
*/
beginN8nOAuth2Flow(resourceUrl: string, metadata?: Record<string, string>): Promise<string>;
/**
* Completes the flow started by `beginN8nOAuth2Flow` once the AS redirects back to
* the trigger URL with `?code&state`. Consumes the one-time `state`, verifies PKCE,
* exchanges the code for an access token **server-side** (the code never reaches the
* sandboxed form page), and validates it. Returns the token and resolved user on
* success, or a failure reason.
*/
completeN8nOAuth2Flow(code: string, state: string): Promise<N8nOAuth2FlowResult>;
/**
* Trades the refresh token from a completed flow for a fresh access/refresh pair on
* the same grant, keeping a long-lived page working past the access token's one-hour
* life without a new redirect. The AS rotates the refresh token, so the caller must
* store the returned one and drop the old one. `resourceUrl` must name the resource
* the grant was approved for.
*/
refreshN8nOAuth2Flow(refreshToken: string, resourceUrl: string): Promise<N8nOAuth2RefreshResult>;
/**
* Verifies an AS access token against `resourceUrl` (the expected audience) without
* running a redirect flow. Used by resource-server triggers (MCP) that receive a
* bearer token directly, and by browser triggers on the POST leg to re-check the
* token the page presents. Returns validity + resolved user, or a failure reason
* (`invalid_token`, `insufficient_scope`, …).
*/
validateN8nOAuth2Token(token: string, resourceUrl: string): Promise<N8nOAuth2ValidationResult>;
/**
* Binds the verified submitter to the current execution: builds an encrypted
* credential context from `token`/`resource` and threads it into the execution's
* runtime context, so downstream nodes resolve *that user's* per-user credentials.
* Call only after the token is validated. The identity persists for the whole
* execution (including across a Wait), within the token's validity window.
*/
establishTriggerIdentity(token: string, resource: string, subject?: string): Promise<void>;
/**
* Checks the status of the triggering identity's resolvable (end-user) credentials
* for this workflow, using the execution context established by
* `establishTriggerIdentity`. Returns connection URLs for any missing credential, or
* `undefined` when no check applies (dynamic-credentials disabled or no identity
* established). Used by the MCP trigger to gate a tool call, by the Form trigger to
* gate a submission, and by the Chat trigger to gate a message send, before an
* execution is enqueued.
*/
checkTriggerCredentialStatus(): Promise<CredentialCheckResult | undefined>;
getInputConnectionData(
connectionType: AINodeConnectionType,
itemIndex: number,
/** A `number` is the never-implemented `inputIndex`, still accepted and ignored so existing nodes keep compiling. */
options?: number | { inputData?: IDataObject },
): Promise<unknown>;
getNodeParameter(
parameterName: string,
fallbackValue?: any,
options?: IGetNodeParameterOptions,
): NodeParameterValueType | object;
/** Always the production endpoint, whichever endpoint the request arrived on. */
getNodeWebhookUrl: (name: WebhookType) => string | undefined;
/**
* The endpoint actually being served (`/webhook-test/…` on a test run), which
* identifies the webhook as an OAuth protected resource. Minting and verifying a
* token both derive the resource from it, so it must track the endpoint served.
*/
getWebhookResourceUrl: (name: WebhookType) => string | undefined;
evaluateExpression(expression: string, itemIndex?: number): NodeParameterValueType;
getParamsData(): object;
getQueryData(): object;
getRequestObject(): express.Request;
getResponseObject(): express.Response;
getWebhookName(): string;
/** Whether this request arrived on the editor's session-scoped canvas chat test route. */
isChatSessionTest(): boolean;
validateCookieAuth(cookieValue: string): Promise<IUser>;
/**
* The n8n user who started this test run, recorded on the webhook registration.
* Only test webhooks carry it, so this resolves to `undefined` in production.
*
* Optional so hosts that implement this interface themselves are not forced to
* supply it; call it as `getTestWebhookUser?.()`.
*/
getTestWebhookUser?(): Promise<IUser | undefined>;
/** Emits telemetry for an advanced HITL response actioned via this webhook. */
logHitlResponse(payload: { approved: boolean; authorized: boolean }): void;
nodeHelpers: NodeHelperFunctions;
helpers: RequestHelperFunctions & BaseHelperFunctions & BinaryHelperFunctions;
}
export interface INodeCredentialsDetails {
id: string | null;
name: string;
/**
* Set automatically when the credential slot is managed by the n8n AI Gateway.
* Not intended to be set or modified manually.
*/
__aiGatewayManaged?: boolean;
}
export interface INodeCredentials {
[key: string]: INodeCredentialsDetails;
}
export type ICustomTelemetryTag = {
key: string;
value: string;
};
export type OnError = 'continueErrorOutput' | 'continueRegularOutput' | 'stopWorkflow';
export interface INode {
id: string;
name: string;
typeVersion: number;
type: string;
position: [number, number];
disabled?: boolean;
notes?: string;
notesInFlow?: boolean;
retryOnFail?: boolean;
maxTries?: number;
waitBetweenTries?: number;
alwaysOutputData?: boolean;
executeOnce?: boolean;
onError?: OnError;
continueOnFail?: boolean;
customTelemetryTags?: {
tag?: ICustomTelemetryTag[];
};
parameters: INodeParameters;
credentials?: INodeCredentials;
webhookId?: string;
extendsCredential?: string;
rewireOutputLogTo?: NodeConnectionType;
// forces the node to execute a particular custom operation
// based on resource and operation
// instead of calling default execute function
// used by evaluations test-runner
forceCustomOperation?: {
resource: string;
operation: string;
};
}
export interface IPinData {
[nodeName: string]: INodeExecutionData[];
}
export interface INodes {
[key: string]: INode;
}
export interface IObservableObject {
[key: string]: any;
__dataChanged: boolean;
}
export interface IBinaryKeyData {
[key: string]: IBinaryData;
}
export interface IPairedItemData {
item: number;
input?: number; // If undefined "0" gets used
sourceOverwrite?: ISourceData;
}
export const ChatNodeMessageType = {
WITH_BUTTONS: 'with-buttons',
} as const;
export type ChatNodeMessageButtonType = 'primary' | 'secondary';
export type ChatNodeMessageWithButtons = {
type: typeof ChatNodeMessageType.WITH_BUTTONS;
text: string;
blockUserInput: boolean;
buttons: Array<{
text: string;
link: string;
type: ChatNodeMessageButtonType;
}>;
};
export type ChatNodeMessage = ChatNodeMessageWithButtons | string;
/**
* Technical metadata preserved when an error is redacted.
* Contains only non-PII debugging information.
* Deliberately excludes: message, description, messages[], cause,
* context, and errorResponse — all of which may contain PII.
*/
export interface IRedactedErrorInfo {
/** Constructor name of the original error class, e.g. 'NodeApiError' */
type: string;
/** HTTP status code from NodeApiError, e.g. '404', '500', null */
httpCode?: string | null;
}
export interface INodeExecutionRedactionInfo {
redacted: true;
reason: string;
/**
* When present, indicates the item's `error` field was redacted.
* Contains only non-PII technical metadata. Placeholder for future smart filtering.
*/
error?: IRedactedErrorInfo;
}
/**
* Marker placed on an `item.json` field when it has been redacted by a
* node-declared field redaction strategy. A typed object (not `null` or a
* string) so consumers can distinguish intentional redaction from absent data.
*
* `canReveal` controls whether the UI should offer an option to unmask the
* value. For credential-equivalent fields (e.g. auth headers) this is always
* `false`; future strategies may produce `true` for less-sensitive fields.
*/
export interface IRedactedFieldMarker {
__redacted: true;
reason: 'node_defined_field';
canReveal: boolean;
}
export interface INodeExecutionData {
[key: string]:
| IDataObject
| IBinaryKeyData
| INodeExecutionRedactionInfo
| IPairedItemData
| IPairedItemData[]
| NodeApiError
| NodeOperationError
| number
| string
| undefined;
/**
* JSON output.
* In `continueErrorOutput` mode, engine will try to read item.error or fallback to item.json.error with allowed optional keys: message, details.
*/
json: IDataObject;
binary?: IBinaryKeyData;
error?: NodeApiError | NodeOperationError;
pairedItem?: IPairedItemData | IPairedItemData[] | number;
metadata?: {
subExecution: RelatedExecution;
};
evaluationData?: Record<string, GenericValue>;
/**
* Redaction marker. Present when this item's data has been redacted.
* Check `redaction.redacted` to determine if data was stripped,
* and `redaction.reason` for why (e.g. "workflow_redaction_policy").
*/
redaction?: INodeExecutionRedactionInfo;
/**
* Use this key to send a message to the chat.
*
* - Workflow has to be started by a chat node.
* - Put execution to wait after sending.
*
* See example in
* packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/Chat.node.ts
*/
sendMessage?: ChatNodeMessage;
/**
* @deprecated This key was added by accident and should not be used as it
* will be removed in future. For more information see PR #12469.
*/
index?: number;
}
export type NodeParameterValue = string | number | boolean | undefined | null;
export type ResourceLocatorModes = 'id' | 'url' | 'list' | string;
export interface IResourceLocatorResult {
name: string;
value: string;
url?: string;
}
export interface INodeParameterResourceLocator {
__rl: true;
mode: ResourceLocatorModes;
value: Exclude<NodeParameterValue, boolean>;
cachedResultName?: string;
cachedResultUrl?: string;
__regex?: string;
}
export type IconOrEmoji = { type: 'icon'; value: string } | { type: 'emoji'; value: string };
export type NodeParameterValueType =
// TODO: Later also has to be possible to add multiple ones with the name name. So array has to be possible
| NodeParameterValue
| INodeParameters
| INodeParameterResourceLocator
| ResourceMapperValue
| FilterValue
| AssignmentCollectionValue
| IconOrEmoji
| NodeParameterValue[]
| INodeParameters[]
| INodeParameterResourceLocator[]
| ResourceMapperValue[];
export interface INodeParameters {
[key: string]: NodeParameterValueType;
}
export type NodePropertyTypes =
| 'boolean'
| 'button'
| 'collection'
| 'color'
| 'dateTime'
| 'fixedCollection'
| 'hidden'
| 'icon'
| 'json'
| 'callout'
| 'notice'
| 'multiOptions'
| 'number'
| 'options'
| 'string'
| 'credentialsSelect'
| 'resourceLocator'
| 'curlImport'
| 'resourceMapper'
| 'filter'
| 'assignmentCollection'
| 'credentials'
| 'workflowSelector'
| 'agentSelector';
export type CodeAutocompleteTypes = 'function' | 'functionItem';
export type EditorType = 'codeNodeEditor' | 'jsEditor' | 'htmlEditor' | 'sqlEditor' | 'cssEditor';
export type CodeNodeEditorLanguage = (typeof CODE_LANGUAGES)[number];
export type CodeExecutionMode = (typeof CODE_EXECUTION_MODES)[number];
export type SQLDialect =
| 'StandardSQL'
| 'PostgreSQL'
| 'MySQL'
| 'OracleDB'
| 'MariaSQL'
| 'MSSQL'
| 'SQLite'
| 'Cassandra'
| 'PLSQL';
export interface ILoadOptions {
routing?: {
operations?: IN8nRequestOperations;
output?: INodeRequestOutput;
request?: DeclarativeRestApiSettings.HttpRequestOptions;
};
}
export type NodePropertyAction = {
type: 'askAiCodeGeneration';
handler?: string;
target?: string;
};
export interface CalloutActionBase {
type: string;
label: string;
icon?: string;
}
export interface CalloutActionOpenSampleWorkflowTemplate extends CalloutActionBase {
type: 'openSampleWorkflowTemplate';
templateId: string;
}
export type CalloutAction = CalloutActionOpenSampleWorkflowTemplate;
export interface INodePropertyTypeOptions {
// Supported by: button
buttonConfig?: {
action?: string | NodePropertyAction;
label?: string; // otherwise "displayName" is used
hasInputField?: boolean;
inputFieldMaxLength?: number; // Supported if hasInputField is true
};
containerClass?: string; // Supported by: notice
sectionHeader?: boolean; // Supported by: notice — renders as a section-header divider instead of a notice box
alwaysOpenEditWindow?: boolean; // Supported by: json
codeAutocomplete?: CodeAutocompleteTypes; // Supported by: string
editor?: EditorType; // Supported by: string
editorIsReadOnly?: boolean; // Supported by: string
sqlDialect?: SQLDialect; // Supported by: sqlEditor
loadOptionsDependsOn?: string[]; // Supported by: options
loadOptionsMethod?: string; // Supported by: options
loadOptions?: ILoadOptions; // Supported by: options
maxValue?: number; // Supported by: number
minValue?: number; // Supported by: number
multipleValues?: boolean; // Supported by: <All>
multipleValueButtonText?: string; // Supported when "multipleValues" set to true
numberPrecision?: number; // Supported by: number
fixedCollection?: {
itemTitle?: string; // Template for item titles, supports {{ $collection.item.value }}, {{ $collection.item.index }}
layout?: 'inline'; // Render sub-parameters side-by-side in a row
};
password?: boolean; // Supported by: string
copyButton?: boolean; // Supported by: string — renders a readonly value with a click-to-copy affordance
redactJsonLeaves?: boolean; // Supported by: json (credential fields only) — redacts leaf values instead of the whole field
resolveCredentialJsonLeaves?: boolean; // Supported by: json (credential fields only) — resolves expressions in JSON leaf values
ignoreCredentialExpressionResolveError?: boolean; // Supported by credentials fields outside execution contexts
rows?: number; // Supported by: string
showAlpha?: boolean; // Supported by: color
sortable?: boolean; // Supported when "multipleValues" set to true
expirable?: boolean; // Supported by: hidden (only in the credentials)
dateOnly?: boolean; // Supported by: dateTime
resourceMapper?: ResourceMapperTypeOptions;
filter?: FilterTypeOptions;
assignment?: AssignmentTypeOptions;
minRequiredFields?: number; // Supported by: fixedCollection
maxAllowedFields?: number; // Supported by: fixedCollection
hideOptionalFields?: boolean; // Supported by: fixedCollection - hide non-required fields by default
addOptionalFieldButtonText?: string; // Supported by: fixedCollection with hideOptionalFields set to true
showEvenWhenOptional?: boolean; // Supported by: fixedCollection with hideOptionalFields
calloutAction?: CalloutAction; // Supported by: callout
binaryDataProperty?: boolean; // Indicate that the property expects binary data
[key: string]: any;
}
export interface ResourceMapperTypeOptionsBase {
mode: 'add' | 'update' | 'upsert' | 'map';
valuesLabel?: string;
fieldWords?: {
singular: string;
plural: string;
};
addAllFields?: boolean;
noFieldsError?: string;
multiKeyMatch?: boolean;
supportAutoMap?: boolean;
hideNoDataError?: boolean; // Hide "No data found" error when no fields are available
matchingFieldsLabels?: {
title?: string;
description?: string;
hint?: string;
};
showTypeConversionOptions?: boolean;
// When true, values mapped to string-typed schema fields are always cast to
// string during validation, and the `convertFieldsToString` field stored in the
// resource mapper value is ignored. That stored field predates this option and
// was never user-editable: the UI wrote it unconditionally, so only
// programmatic authors could produce a differing value.
alwaysConvertFieldsToString?: boolean;
allowEmptyValues?: boolean;
// When true, a cached schema that is detected to be structurally incomplete
// (e.g. authored by an AI builder rather than loaded from the source) is
// reconciled against the source on node open. A complete-but-drifted schema
// still shows the stale-data warning, leaving the refresh up to the user.
refreshIncompleteSchemaOnOpen?: boolean;
// When true, a complete-but-drifted cached schema is reconciled against the
// live source on node open / tab focus (e.g. subworkflow trigger inputs).
refreshStaleSchemaOnOpen?: boolean;
}
// Enforce at least one of resourceMapperMethod or localResourceMapperMethod
export type ResourceMapperTypeOptionsLocal = {
resourceMapperMethod: string;
localResourceMapperMethod?: never; // Explicitly disallows this property
};
export type ResourceMapperTypeOptionsExternal = {
localResourceMapperMethod: string;
resourceMapperMethod?: never; // Explicitly disallows this property
};
export type ResourceMapperTypeOptions = ResourceMapperTypeOptionsBase &
(ResourceMapperTypeOptionsLocal | ResourceMapperTypeOptionsExternal);
type NonEmptyArray<T> = [T, ...T[]];
export type FilterTypeCombinator = 'and' | 'or';
export type FilterTypeOptions = {
version: 1 | 2 | 3 | {}; // required so nodes are pinned on a version
caseSensitive?: boolean | string; // default = true
leftValue?: string; // when set, user can't edit left side of condition
allowedCombinators?: NonEmptyArray<FilterTypeCombinator>; // default = ['and', 'or']
maxConditions?: number; // default = 10
typeValidation?: 'strict' | 'loose' | {}; // default = strict, `| {}` is a TypeScript trick to allow custom strings (expressions), but still give autocomplete
};
export type AssignmentTypeOptions = Partial<{
hideType?: boolean; // visible by default
defaultType?: FieldType | 'string';
disableType?: boolean; // visible by default
}>;
export type DisplayCondition =
| { _cnd: { eq: NodeParameterValue } }
| { _cnd: { not: NodeParameterValue } }
| { _cnd: { gte: number | string } }
| { _cnd: { lte: number | string } }
| { _cnd: { gt: number | string } }
| { _cnd: { lt: number | string } }
| { _cnd: { between: { from: number | string; to: number | string } } }
| { _cnd: { startsWith: string } }
| { _cnd: { endsWith: string } }
| { _cnd: { includes: string } }
| { _cnd: { regex: string } }
| { _cnd: { exists: true } };
export type NodeFeatures = Record<string, boolean>;
export type FeatureCondition = { '@version': Array<number | DisplayCondition> };
export type NodeFeaturesDefinition = Record<string, FeatureCondition>;
export type DeploymentCondition = 'cloud' | 'hosted';
export interface IDisplayOptions {
hide?: {
[key: string]: Array<NodeParameterValue | DisplayCondition> | undefined;
};
show?: {
'@version'?: Array<number | DisplayCondition>;
'@feature'?: Array<string | DisplayCondition>;
'@tool'?: boolean[];
[key: string]: Array<NodeParameterValue | DisplayCondition> | undefined;
};
showOnDeployment?: DeploymentCondition;
/**
* @deprecated Use showOnDeployment instead
*/
hideOnCloud?: boolean;
}
export interface ICredentialsDisplayOptions {
hide?: {
[key: string]: NodeParameterValue[] | undefined;
};
show?: {
'@version'?: number[];
[key: string]: NodeParameterValue[] | undefined;
};
showOnDeployment?: DeploymentCondition;
/**
* @deprecated Use showOnDeployment instead
*/
hideOnCloud?: boolean;
}
export interface INodeProperties {
displayName: string;
name: string;
type: NodePropertyTypes;
typeOptions?: INodePropertyTypeOptions;
default: NodeParameterValueType;
description?: string;
hint?: string;
builderHint?: IParameterBuilderHint;
disabledOptions?: IDisplayOptions;
displayOptions?: IDisplayOptions;
/**
* Hides the property unless the named instance-level env feature flag is
* truthy. The value is the flag suffix without the `N8N_ENV_FEAT_` prefix
*/
envFeatureFlag?: Uppercase<string>;
options?: Array<INodePropertyOptions | INodeProperties | INodePropertyCollection>;
placeholder?: string;
isNodeSetting?: boolean;
noDataExpression?: boolean;
required?: boolean;
routing?: INodePropertyRouting;
credentialTypes?: Array<
'extends:oAuth2Api' | 'extends:oAuth1Api' | 'has:authenticate' | 'has:genericAuth'
>;
extractValue?: INodePropertyValueExtractor;
modes?: INodePropertyMode[];
requiresDataPath?: 'single' | 'multiple';
doNotInherit?: boolean;
// set expected type for the value which would be used for validation and type casting
validateType?: FieldType;
// works only if validateType is set
// allows to skip validation during execution or set custom validation/casting logic inside node
// inline error messages would still be shown in UI
ignoreValidationDuringExecution?: boolean;
// for type: options | multiOptions – skip validation of the value (e.g. when value is not in the list and specified via expression)
allowArbitraryValues?: boolean;
// This field indicates that the field is a resolvable field that should be resolved in dynamic credential setup
resolvableField?: boolean;
}
export interface INodePropertyModeTypeOptions {
searchListMethod?: string; // Supported by: options
searchFilterRequired?: boolean;
searchable?: boolean;
/**
* If provided, a slow load notice will be shown to the user if the resource locator takes longer than the timeout to load.
* @example
* {
* message: 'If loading takes longer than expected, try selecting a resource using "By ID"',
* timeout: 10000,
* }
*/
slowLoadNotice?: { message: string; timeout: number };
/**
* If true, the resource locator will not show an error if the credentials are not selected
*/
skipCredentialsCheckInRLC?: boolean;
allowNewResource?: {
label: string;
} & (
| { method: string; url?: never; defaultName: string }
| { method?: never; url: string; defaultName?: never }
);
}
export interface INodePropertyMode {
displayName: string;
name: string;
type: 'string' | 'list';
hint?: string;
validation?: Array<
INodePropertyModeValidation | { (this: IExecuteSingleFunctions, value: string): void }
>;
placeholder?: string;
url?: string;
extractValue?: INodePropertyValueExtractor;
initType?: string;
entryTypes?: {
[name: string]: {
selectable?: boolean;
hidden?: boolean;
queryable?: boolean;
data?: {
request?: IHttpRequestOptions;
output?: INodeRequestOutput;
};
};
};
search?: INodePropertyRouting;
typeOptions?: INodePropertyModeTypeOptions;
}
export interface INodePropertyModeValidation {
type: string;
properties: {};
}
export interface INodePropertyRegexValidation extends INodePropertyModeValidation {
type: 'regex';
properties: {
regex: string;
errorMessage: string;
};
}
export interface INodePropertyOptions {
name: string;
value: string | number | boolean;
action?: string;
description?: string;
disabled?: boolean;
builderHint?: IParameterBuilderHint;
routing?: INodePropertyRouting;
outputConnectionType?: NodeConnectionType;
inputSchema?: any;
displayOptions?: IDisplayOptions;
// disabledOptions added for compatibility with INodeProperties and INodeCredentialDescription types
// it needs to be implemented, if needed
disabledOptions?: undefined;
// When set, the option is hidden in the editor unless the matching
// `N8N_ENV_FEAT_<envFeatureFlag>` flag is enabled.
envFeatureFlag?: Uppercase<string>;
}
export interface INodeListSearchItems extends INodePropertyOptions {
icon?: string;
url?: string;
}
export interface INodeListSearchResult {
results: INodeListSearchItems[];
paginationToken?: string;
}
export interface INodePropertyCollection {
displayName: string;
name: string;
values: INodeProperties[];
builderHint?: IParameterBuilderHint;
}
/**
* One variation of `extraTypeDefContent`, optionally gated by `displayOptions`
* so per-mode / per-resource / per-operation examples land only in their
* corresponding narrowed type without cross-bleed.
*/
export interface IBuilderHintVariation {
content: string;
displayOptions?: IDisplayOptions;
}
export interface IParameterBuilderHint {
propertyHint: string;
placeholderSupported?: boolean;
}
export interface INodePropertyValueExtractorBase {
type: string;
}
export interface INodePropertyValueExtractorRegex extends INodePropertyValueExtractorBase {
type: 'regex';
regex: string | RegExp;
}
export interface INodePropertyValueExtractorFunction {
(
this: IExecuteSingleFunctions,
value: string | NodeParameterValue,
): Promise<string | NodeParameterValue> | string;
}
export type INodePropertyValueExtractor = INodePropertyValueExtractorRegex;
export interface IParameterDependencies {
[key: string]: string[];
}
export type IParameterLabel = {
size?: 'small' | 'medium';
};
export interface ITriggerResponse {
closeFunction?: CloseFunction;
// To manually trigger the run
manualTriggerFunction?: () => Promise<void>;
// Gets added automatically at manual workflow runs resolves with
// the first emitted data
manualTriggerResponse?: Promise<INodeExecutionData[][]>;
}
/** Private-credential usage a sub-execution reports to its caller, so the calling node's task inherits the flags and redaction covers the embedded output. */
export interface DynamicCredentialsUsage {
/** True when the run resolved a private credential. */
usedDynamicCredentials?: boolean;
/** True when the run attempted to resolve a private credential (telemetry superset of `usedDynamicCredentials`). */
attemptedDynamicCredentials?: boolean;
/** The n8n user a resolved private credential belonged to; keeps the parent execution revealable to that user. */
dynamicCredentialsResolvedUserId?: string;
}
export interface ExecuteWorkflowData extends DynamicCredentialsUsage {
executionId: string;
/** Terminal node output: items from every run concatenated per output branch, unless the caller sets `returnLastRunOnly`. */
data: Array<INodeExecutionData[] | null>;
waitTill?: Date | null;
}
/**
* Inline agent definition embedded in the calling node's parameters. Kept
* structurally loose here (this package cannot depend on `@n8n/api-types`);
* the execution layer validates it against `InlineAgentConfigSchema`.
*/
export interface InlineAgentPayload {
config: {
name?: string;
model?: string;
credential?: string;
instructions?: string;
tools?: IDataObject[];
mcpServers?: IDataObject[];
skills?: Array<{ type?: string; id?: string }>;
};
/** Skill bodies keyed by the ids `config.skills` references. */
skills?: Record<string, IDataObject>;
}
/** Which agent to execute: a saved agent by id, or an inline definition. */
export type ExecuteAgentSource =
| { agentId: string; inlineAgent?: never }
| { agentId?: never; inlineAgent: InlineAgentPayload };
export type ExecuteAgentInfo = ExecuteAgentSource & {
/**
* Optional caller-supplied session id. When set, this becomes the agent
* thread id, letting workflows continue the same conversation (and reuse
* memory) across executions. When omitted, a per-call thread is derived
* from the workflow execution id and item index. Applies to inline agents
* too: their conversation threads persist, even though their runs don't
* appear in the sessions UI.
*/
sessionId?: string;
/**
* Optional JSON Schema describing the shape the agent should return. When
* provided, it is applied to this call only, and the conforming object is
* surfaced on {@link ExecuteAgentData.structuredOutput}.
*/
outputSchema?: JSONSchema7;
/**
* Whether to forward generated text to a streaming workflow response. Defaults
* to true when omitted. This does not affect execution progress events.
*/
enableStreaming?: boolean;
/**
* Which slice of the calling node's input the agent's `fetch_input_data`
* tool should expose: the single current item (`'item'`, default) or all
* input items (`'all'`, used when the node invokes the agent once for the
* whole batch).
*/
inputDataScope?: 'item' | 'all';
/**
* When true, the agent additionally gets the `fetch_workflow_context` tool,
* which can read any executed node's output. Off by default.
*/
exposeWorkflowData?: boolean;
};
/**
* Context about the calling workflow execution, passed to the agent runtime so
* it can expose the `fetch_input_data` and (opt-in) `fetch_workflow_context` tools to the agent. The
* `runExecutionData` is a live reference to the calling execution's run data —
* must be treated as read-only.
*/
export interface ExecuteAgentWorkflowContext {
workflowId?: string;
workflowName?: string;
/** Name of the node that invoked the agent */
callingNodeName: string;
/** ID of the node that invoked the agent */
callingNodeId?: string;
/** The calling node's input items, already scoped per {@link ExecuteAgentInfo.inputDataScope}. */
inputData?: INodeExecutionData[];
/** Which slice {@link inputData} represents. */
inputDataScope?: 'item' | 'all';
/** Whether to attach the `fetch_workflow_context` tool. */
exposeWorkflowData?: boolean;
/**
* Whether the caller supplied an explicit session id (vs the derived
* per-call thread id). Inline agents persist conversation memory only when
* true — a derived thread is never continued, so persisting it would only
* accumulate unreachable rows.
*/
hasCallerSessionId?: boolean;
/** Name and type of every node in the calling workflow. */
nodes: Array<{ name: string; type: string }>;
/** The calling execution's run data (read-only by convention). */
runExecutionData: IRunExecutionData;
}
export interface ExecuteAgentInvocationContext {
nodeId: string;
nodeName: string;
runIndex: number;
itemIndex: number;
sendResponseChunk?: (type: ChunkType, content?: string) => Promise<void>;
}
export interface ExecuteAgentOptions {
node?: INode;
parentWorkflowId: string;
parentWorkflowSettings?: IWorkflowSettings;
executionMode?: WorkflowExecuteMode;
}
export interface ExecuteAgentData {
/** The agent's text response extracted from the last assistant message. */
response: string;
/** Parsed structured output if the agent has a structuredOutput schema. */
structuredOutput: unknown | null;
/** Token usage for the call. */
usage: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
} | null;
/** Tool calls the agent made during execution. */
toolCalls: Array<{
toolName: string;
input: unknown;
result: unknown;
}>;
/** Why the agent stopped. */
finishReason: string;
/**
* Identifiers of the agent session this call wrote to. Surfaced so the
* caller (e.g. the MessageAnAgent node) can link from a workflow execution
* back to the agent session detail view. `null` for inline agents, whose
* runs are not recorded as sessions.
*/
session: {
agentId: string;
projectId: string;
/**
* Caller-facing session id: the override the caller supplied, or the
* derived per-call id. Safe to feed back into the node's Session ID
* parameter to continue the conversation.
*/
sessionId: string;
/** The internally scoped thread id the session was persisted under (deep-link key). */
threadId: string;
} | null;
}
export type WebhookSetupMethodNames = 'checkExists' | 'create' | 'delete';
export namespace MultiPartFormData {
export interface File {
/** Parser-owned temporary path. Consume it in the webhook function or its response stream. */
filepath: string;
mimetype?: string;
originalFilename?: string;
newFilename: string;
size?: number;
}
export type Request = express.Request<
{},
{},
{
data: Record<string, string | string[]>;
files: Record<string, File | File[]>;
}
>;
}
export interface SupplyData {
metadata?: IDataObject;
response: unknown;
closeFunction?: CloseFunction;
hints?: NodeExecutionHint[];
}
export type NodeOutput =
| INodeExecutionData[][]
| NodeExecutionWithMetadata[][]
| EngineRequest
| null;
export interface INodeType {
description: INodeTypeDescription;
supplyData?(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData>;
execute?(this: IExecuteFunctions, response?: EngineResponse): Promise<NodeOutput>;
/**
* A function called when a node receives a chat message. Allows it to react
* to the message before it gets executed.
*/
onMessage?(context: IExecuteFunctions, data: INodeExecutionData): Promise<NodeOutput>;
poll?(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
trigger?(this: ITriggerFunctions): Promise<ITriggerResponse | undefined>;
webhook?(this: IWebhookFunctions): Promise<IWebhookResponseData>;
methods?: {
loadOptions?: {
[key: string]: (this: ILoadOptionsFunctions) => Promise<INodePropertyOptions[]>;
};
listSearch?: {
[key: string]: (
this: ILoadOptionsFunctions,
filter?: string,
paginationToken?: string,
) => Promise<INodeListSearchResult>;
};
credentialTest?: {
// Contains a group of functions that test credentials.
[functionName: string]: ICredentialTestFunction;
};
resourceMapping?: {
[functionName: string]: (this: ILoadOptionsFunctions) => Promise<ResourceMapperFields>;
};
localResourceMapping?: {
[functionName: string]: (this: ILocalLoadOptionsFunctions) => Promise<ResourceMapperFields>;
};
actionHandler?: {
[functionName: string]: (
this: ILoadOptionsFunctions,
payload: IDataObject | string | undefined,
) => Promise<NodeParameterValueType>;
};
};
webhookMethods?: {
[name in WebhookType]?: {
[method in WebhookSetupMethodNames]: (this: IHookFunctions) => Promise<boolean>;
};
};
/**
* Defines custom operations for nodes that do not implement an `execute` method, such as declarative nodes.
* This function will be invoked instead of `execute` for a specific resource and operation.
* Should be either `execute` or `customOperations` defined for a node, but not both.
*
* @property customOperations - Maps specific resource and operation to a custom function
*/
customOperations?: {
[resource: string]: {
[operation: string]: (this: IExecuteFunctions) => Promise<NodeOutput>;
};
};
}
/**
* Represents a request to execute a specific node and receive the result back.
* This action tells the engine to execute the specified node with the provided input
* and then call back the requesting node with the execution result.
*
* @template T - The type of metadata associated with this action
*/
type ExecuteNodeAction<T> = {
/** The type identifier for this action */
actionType: 'ExecutionNodeAction';
/** The name of the node to be executed */
nodeName: string;
/** Input data to be passed to the node for execution */
input: IDataObject;
/** The type of connection this execution request uses */
type: NodeConnectionType;
/** Unique identifier for this execution request */
id: string;
/** Additional metadata for this execution request */
metadata: T;
};
/**
* Union type of all possible actions that nodes can request from the workflow engine.
* Currently only contains ExecuteNodeAction, but will be extended with additional
* action types as they are implemented.
*
* @template T - The type of metadata associated with this action
*/
type EngineAction<T = unknown> = ExecuteNodeAction<T>;
/**
* A collection of actions that a node wants the engine to fulfill and call back with results.
* The requesting node sends this to the engine and expects to receive an EngineResponse
* containing the results of all requested actions.
*
* @template T - The type of metadata associated with this request
*/
export type EngineRequest<T = object> = {
/** Array of actions that the requesting node wants the engine to fulfill */
actions: Array<EngineAction<T>>;
/** Metadata associated with this request */
metadata: T;
};
/**
* Result of executing a single node action within the workflow engine.
* Contains the original action and the resulting task data.
*
* @template T - The type of metadata associated with this result
*/
export type ExecuteNodeResult<T = unknown> = {
/** The action that was executed */
action: ExecuteNodeAction<T>;
/** The resulting task data from the execution */
data: ITaskData;
};
/**
* Union type of all possible results from engine actions.
* Currently only contains ExecuteNodeResult, but will be extended with additional
* result types as new action types are implemented.
*
* @template T - The type of metadata associated with this result
*/
export type EngineResult<T> = ExecuteNodeResult<T>;
/**
* Response structure returned from the workflow engine after execution.
* Contains the results of all executed actions along with associated metadata.
*
* @template T - The type of metadata associated with this response
*/
export type EngineResponse<T = unknown> = {
/** Array of results from each executed action */
actionResponses: Array<EngineResult<T>>;
/** Metadata associated with this response */
metadata: T;
};
/**
* Tag identifying `Node` subclasses. `Symbol.for` interns into the global symbol
* registry, so every copy of `n8n-workflow` loaded in the same process resolves the identical
* symbol, regardless of the dependency tree layout.
*/
const NODE_CLASS_TAG: unique symbol = Symbol.for('n8n.workflow.NodeClass');
/**
* This class serves as the base for all nodes using the new context API
* having this as a class enables us to identify these instances at runtime
*/
export abstract class Node {
get [NODE_CLASS_TAG](): true {
return true;
}
abstract description: INodeTypeDescription;
execute?(
context: IExecuteFunctions,
response?: EngineResponse,
): Promise<INodeExecutionData[][] | EngineRequest>;
webhook?(context: IWebhookFunctions): Promise<IWebhookResponseData>;
poll?(context: IPollFunctions): Promise<INodeExecutionData[][] | null>;
}
/**
* Returns `true` when `nodeType` is an instance of a `Node` subclass (a node using the new
* context API). Prefer this over `nodeType instanceof Node` at runtime: it survives
* `n8n-workflow` module duplication, which breaks `instanceof`.
*/
export function isNodeClassInstance(nodeType: unknown): nodeType is Node {
return typeof nodeType === 'object' && nodeType !== null && NODE_CLASS_TAG in nodeType;
}
export interface IVersionedNodeType {
nodeVersions: {
[key: number]: INodeType;
};
currentVersion: number;
description: INodeTypeBaseDescription;
getNodeType: (version?: number) => INodeType;
}
export interface INodeCredentialTestResult {
status: 'OK' | 'Error';
message: string;
}
export interface INodeCredentialTestRequest {
credentials: ICredentialsDecrypted;
}
export interface INodeCredentialDescription {
name: string;
required?: boolean;
displayName?: string;
disabledOptions?: ICredentialsDisplayOptions;
displayOptions?: ICredentialsDisplayOptions;
testedBy?: ICredentialTestRequest | string; // Name of a function inside `loadOptions.credentialTest`
}
export type INodeIssueTypes = 'credentials' | 'execution' | 'input' | 'parameters' | 'typeUnknown';
export interface INodeIssueObjectProperty {
[key: string]: string[];
}
export interface INodeIssueData {
node: string;
type: INodeIssueTypes;
value: null | boolean | string | string[] | INodeIssueObjectProperty;
}
export interface INodeIssues {
execution?: boolean;
credentials?: INodeIssueObjectProperty;
input?: INodeIssueObjectProperty;
parameters?: INodeIssueObjectProperty;
typeUnknown?: boolean;
[key: string]: undefined | boolean | INodeIssueObjectProperty;
}
export interface IWorkflowIssues {
[key: string]: INodeIssues;
}
export type ThemeIconColor =
| 'neutral'
| 'gray'
| 'black'
| 'blue'
| 'light-blue'
| 'dark-blue'
| 'sky-blue'
| 'orange'
| 'orange-red'
| 'amber'
| 'rust'
| 'pink-red'
| 'magenta'
| 'red'
| 'light-green'
| 'green'
| 'dark-green'
| 'emerald'
| 'forest-green'
| 'lime'
| 'azure'
| 'teal'
| 'purple'
| 'violet'
| 'lavender'
| 'crimson';
export type Themed<T> = T | { light: T; dark: T };
export type IconRef = `fa:${string}` | `node:${string}`;
export type IconFile = `file:${string}.png` | `file:${string}.svg` | ExpressionString;
export type Icon = IconRef | Themed<IconFile> | IconFile;
type NodeGroupType = 'input' | 'output' | 'organization' | 'schedule' | 'transform' | 'trigger';
export interface INodeTypeBaseDescription {
displayName: string;
name: string;
icon?: Icon;
iconColor?: ThemeIconColor;
iconUrl?: Themed<string>;
/**
* Base path for resolving file icons from expressions.
* Used when icon is an expression that resolves to `file:filename.svg`
* Format: `icons/${packageName}/${nodeDirPath}`
*/
iconBasePath?: string;
badgeIconUrl?: Themed<string>;
group: NodeGroupType[];
description: string;
documentationUrl?: string;
subtitle?: string;
defaultVersion?: number;
codex?: CodexData;
parameterPane?: 'wide';
/**
* Whether the node must be hidden in the node creator panel,
* due to deprecation or as a special case (e.g. Start node)
*/
hidden?: true;
/**
* Whether the node will be wrapped for tool-use by AI Agents,
* optionally replacing provided parts of the description
*/
usableAsTool?: true | UsableAsToolDescription;
/** Hints for workflow-sdk type generation, including explicit AI input requirements */
builderHint?: IBuilderHint;
/** Path to schema directory relative to nodes-base/dist/nodes/ (e.g., "Google/Drive") */
schemaPath?: string;
}
/**
* NodeDescription entries that replace the base node entries when
* the node is used as a tool
*
* Note that the new codex is hardcoded and may not behave as expected
* without additional changes to the implementation.
*/
export type UsableAsToolDescription = {
replacements?: Partial<Omit<INodeTypeBaseDescription, 'usableAsTool'>>;
};
export interface INodePropertyRouting {
operations?: IN8nRequestOperations; // Should be changed, does not sound right
output?: INodeRequestOutput;
request?: DeclarativeRestApiSettings.HttpRequestOptions;
send?: INodeRequestSend;
}
export type PostReceiveAction =
| ((
this: IExecuteSingleFunctions,
items: INodeExecutionData[],
response: IN8nHttpFullResponse,
) => Promise<INodeExecutionData[]>)
| IPostReceiveBinaryData
| IPostReceiveFilter
| IPostReceiveLimit
| IPostReceiveRootProperty
| IPostReceiveSet
| IPostReceiveSetKeyValue
| IPostReceiveSort;
export type PreSendAction = (
this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions,
) => Promise<IHttpRequestOptions>;
export interface INodeRequestOutput {
maxResults?: number | string;
postReceive?: PostReceiveAction[];
}
export interface INodeRequestSend {
preSend?: PreSendAction[];
paginate?: boolean | string; // Where should this life?
property?: string; // Maybe: propertyName, destinationProperty?
propertyInDotNotation?: boolean; // Enabled by default
type?: 'body' | 'query';
value?: string;
}
export interface IPostReceiveBase {
type: string;
enabled?: boolean | string;
properties: {
[key: string]: string | number | boolean | IDataObject;
};
errorMessage?: string;
}
export interface IPostReceiveBinaryData extends IPostReceiveBase {
type: 'binaryData';
properties: {
destinationProperty: string;
};
}
export interface IPostReceiveFilter extends IPostReceiveBase {
type: 'filter';
properties: {
pass: boolean | string;
};
}
export interface IPostReceiveLimit extends IPostReceiveBase {
type: 'limit';
properties: {
maxResults: number | string;
};
}
export interface IPostReceiveRootProperty extends IPostReceiveBase {
type: 'rootProperty';
properties: {
property: string;
};
}
export interface IPostReceiveSet extends IPostReceiveBase {
type: 'set';
properties: {
value: string;
};
}
export interface IPostReceiveSetKeyValue extends IPostReceiveBase {
type: 'setKeyValue';
properties: {
[key: string]: string | number;
};
}
export interface IPostReceiveSort extends IPostReceiveBase {
type: 'sort';
properties: {
key: string;
};
}
export const NodeConnectionTypes = {
AiAgent: 'ai_agent',
AiChain: 'ai_chain',
AiDocument: 'ai_document',
AiEmbedding: 'ai_embedding',
AiLanguageModel: 'ai_languageModel',
AiMemory: 'ai_memory',
AiOutputParser: 'ai_outputParser',
AiRetriever: 'ai_retriever',
AiReranker: 'ai_reranker',
AiTextSplitter: 'ai_textSplitter',
AiTool: 'ai_tool',
AiVectorStore: 'ai_vectorStore',
Main: 'main',
} as const;
export type NodeConnectionType = (typeof NodeConnectionTypes)[keyof typeof NodeConnectionTypes];
export type AINodeConnectionType = Exclude<NodeConnectionType, typeof NodeConnectionTypes.Main>;
export const nodeConnectionTypes: NodeConnectionType[] = Object.values(NodeConnectionTypes);
export interface INodeFilter {
// TODO: Later add more filter options like categories, subcatogries,
// regex, allow to exclude certain nodes, ... ?
// Potentially change totally after alpha/beta. Is not a breaking change after all.
nodes?: string[]; // Allowed nodes
excludedNodes?: string[];
}
/**
* @deprecated Use INodeFilter instead
*/
export type INodeInputFilter = INodeFilter;
export interface INodeInputConfiguration {
category?: string;
displayName?: string;
required?: boolean;
type: NodeConnectionType;
filter?: INodeFilter;
maxConnections?: number;
}
export interface INodeOutputConfiguration {
category?: 'error';
displayName?: string;
maxConnections?: number;
required?: boolean;
type: NodeConnectionType;
filter?: INodeFilter;
}
export type ExpressionString = `={{${string}}}`;
export type NodeDefaults = Partial<{
/**
* @deprecated Use {@link INodeTypeBaseDescription.iconColor|iconColor} instead. `iconColor` supports dark mode and uses preset colors from n8n's design system.
*/
color: string;
name: string;
}>;
/**
* Configuration for a single AI subnode input in builderHint.inputs
*/
export interface IBuilderHintInputConfig {
/** Whether this AI input is required */
required: boolean;
/** Conditions under which this input should be available/required */
displayOptions?: IDisplayOptions;
}
/**
* Maps AI input types (e.g. 'ai_languageModel', 'ai_memory') to their configuration
*/
export type BuilderHintInputs = Partial<Record<AINodeConnectionType, IBuilderHintInputConfig>>;
/**
* Configuration for a single output in builderHint.outputs.
*
* Describes when an output connection type is available based on the node's parameters,
* mirroring `IBuilderHintInputConfig`. Unlike inputs, the connection-type key here may also
* be `'main'`, since main outputs are commonly mode-conditional (e.g. vector stores expose
* `main` only in `insert`/`load` modes and `ai_vectorStore` only in `retrieve` mode).
*/
export interface IBuilderHintOutputConfig {
/**
* Whether this output is required to be connected. Optional because most outputs
* are availability declarations only — but some sub-node-style outputs (e.g.
* `ai_vectorStore` in `mode: 'retrieve'`) are useless unless wired to a parent.
*/
required?: boolean;
/** Conditions under which this output is exposed by the node. */
displayOptions?: IDisplayOptions;
}
/**
* Maps connection types (including `main`) to their availability configuration.
*/
export type BuilderHintOutputs = Partial<Record<NodeConnectionType, IBuilderHintOutputConfig>>;
/**
* Related node with explanation of why it's related
*/
export interface IRelatedNode {
/** The node type ID (e.g., '@n8n/n8n-nodes-langchain.memoryBufferWindow') */
nodeType: string;
/** Brief explanation of why this node is related (e.g., 'Maintains conversation history') */
relationHint: string;
}
/**
* Builder hints for workflow-sdk type generation
*/
export interface IBuilderHint {
/** Explicit AI input requirements for accurate type generation */
inputs?: BuilderHintInputs;
/** Declarative output availability — which outputs the node exposes per parameter values */
outputs?: BuilderHintOutputs;
/** General hint message for LLM workflow builders */
searchHint?: string;
/** Related nodes that work together with this node */
relatedNodes?: IRelatedNode[];
/**
* Multi-line content (typically code examples wrapped in `<patterns>...</patterns>`)
* emitted into the generated workflow-sdk `.d.ts` but NOT surfaced in
* `nodes(action="search")` results. Each variation may carry `displayOptions`
* so per-mode / per-resource / per-operation examples land only in their
* corresponding narrowed type.
*/
extraTypeDefContent?: IBuilderHintVariation[];
}
export interface INodeTypeDescription extends INodeTypeBaseDescription {
version: number | number[];
defaults: NodeDefaults;
eventTriggerDescription?: string;
activationMessage?: string;
inputs: Array<NodeConnectionType | INodeInputConfiguration> | ExpressionString;
requiredInputs?: string | number[] | number; // Ony available with executionOrder => "v1"
inputNames?: string[];
outputs: Array<NodeConnectionType | INodeOutputConfiguration> | ExpressionString;
outputNames?: string[];
properties: INodeProperties[];
credentials?: INodeCredentialDescription[];
maxNodes?: number; // How many nodes of that type can be created in a workflow
polling?: true | undefined;
supportsCORS?: true | undefined;
requestDefaults?: DeclarativeRestApiSettings.HttpRequestOptions;
requestOperations?: IN8nRequestOperations;
hooks?: {
[key: string]: INodeHookDescription[] | undefined;
activate?: INodeHookDescription[];
deactivate?: INodeHookDescription[];
};
webhooks?: IWebhookDescription[];
translation?: { [key: string]: object };
mockManualExecution?: true;
triggerPanel?: TriggerPanelDefinition | boolean;
extendsCredential?: string;
hints?: NodeHint[];
communityNodePackageVersion?: string;
waitingNodeTooltip?: string;
__loadOptionsMethods?: string[]; // only for validation during build
/**
* When true, skip generating a name from resource/operation (e.g., "SendAndWait message in Slack")
* and just use defaults.name. Useful for tool nodes that should keep a simple name like "Slack".
*/
skipNameGeneration?: boolean;
features?: NodeFeaturesDefinition;
/** Hints for workflow-sdk type generation, including explicit AI input requirements */
builderHint?: IBuilderHint;
/**
* Dot-notation paths relative to `item.json` that contain
* credential-equivalent sensitive data (e.g. `'headers.authorization'`).
* These fields are always redacted regardless of workflow policy or user
* permissions and are never revealable.
*/
sensitiveOutputFields?: string[];
}
export type TriggerPanelDefinition = {
hideContent?: boolean | string;
header?: string;
executionsHelp?: string | { active: string; inactive: string };
activationHint?: string | { active: string; inactive: string };
};
/**
* Collapses hints that report the same kind of problem, so a node reporting it
* for 20 fields shows one summary line instead of 20 near-identical callouts.
*/
export type NodeHintGroup = {
/** Hints sharing this key are collapsed together */
key: string;
/** Text shown while collapsed. `{count}` is replaced with the number of hints in the group. */
summary: string;
/**
* Short form listed when the group is expanded, e.g. just the field name.
* Falls back to `message` when not set.
*/
label?: string;
};
export type NodeHint = {
message: string;
type?: 'info' | 'warning' | 'danger';
location?: 'outputPane' | 'inputPane' | 'ndv';
displayCondition?: string;
whenToDisplay?: 'always' | 'beforeExecution' | 'afterExecution';
group?: NodeHintGroup;
};
export type NodeExecutionHint = Omit<NodeHint, 'whenToDisplay' | 'displayCondition'>;
export interface INodeHookDescription {
method: string;
}
export interface IWebhookData {
httpMethod: IHttpRequestMethods;
node: string;
path: string;
webhookDescription: IWebhookDescription;
workflowId: string;
workflowExecuteAdditionalData: IWorkflowExecuteAdditionalData;
webhookId?: string;
isTest?: boolean;
/**
* Set when this test webhook was registered for the editor's canvas chat session
* (path rewritten to `{workflowId}/{chatSessionId}`). Lets the Chat Trigger skip
* webhook auth for that trusted, session-scoped route only — every other test
* request still enforces the configured authentication.
*/
isChatSessionTest?: boolean;
userId?: string;
staticData?: Workflow['staticData'];
}
export type WebhookType = 'default' | 'setup';
/**
* Key under which an {@link IWebhookDescription} holds native (engine-free)
* resolvers for its expression-template fields, keyed by field name. Populated
* by `webhookDescriptionFields()` and read via `resolveWebhookDescriptionField()`.
* Backend-only: not serialized with the description.
*/
export const WEBHOOK_RESOLVERS: unique symbol = Symbol.for('n8n.webhookDescriptionResolvers');
/**
* Native resolvers for a webhook description's fields, keyed by field name.
* Each entry pairs the expression template a field carries with a function
* computing the same value from the node's parameters, without the expression
* engine. Stored under {@link WEBHOOK_RESOLVERS}.
*/
export type NativeParameterResolvers = Record<
string,
{
template: string;
resolve: (parameters: INodeParameters) => NodeParameterValueType | undefined;
}
>;
export interface IWebhookDescription {
[key: string]: IHttpRequestMethods | WebhookResponseMode | boolean | string | undefined;
[WEBHOOK_RESOLVERS]?: NativeParameterResolvers;
httpMethod: IHttpRequestMethods | string;
isFullPath?: boolean;
name: WebhookType;
path: string;
responseBinaryPropertyName?: string;
responseContentType?: string;
responsePropertyName?: string;
responseMode?: WebhookResponseMode | string;
responseData?: WebhookResponseData | string;
restartWebhook?: boolean;
nodeType?: 'webhook' | 'form' | 'mcp';
ndvHideUrl?: string | boolean; // If true the webhook will not be displayed in the editor
ndvHideMethod?: string | boolean; // If true the method will not be displayed in the editor
}
export interface ProxyInput {
all: () => INodeExecutionData[];
context: any;
first: () => INodeExecutionData | undefined;
item: INodeExecutionData | undefined;
last: () => INodeExecutionData | undefined;
params?: INodeParameters;
}
export interface IWorkflowDataProxyData {
[key: string]: any;
$binary: INodeExecutionData['binary'];
$data: any;
$env: any;
$evaluateExpression: (expression: string, itemIndex?: number) => NodeParameterValueType;
$item: (itemIndex: number, runIndex?: number) => IWorkflowDataProxyData;
$items: (nodeName?: string, outputIndex?: number, runIndex?: number) => INodeExecutionData[];
$json: INodeExecutionData['json'];
$node: any;
$parameter: INodeParameters;
$position: number;
$workflow: any;
$: any;
$input: ProxyInput;
$thisItem: any;
$thisRunIndex: number;
$thisItemIndex: number;
$now: any;
$today: any;
$getPairedItem: (
destinationNodeName: string,
incomingSourceData: ISourceData | null,
pairedItem: IPairedItemData,
) => INodeExecutionData | null;
constructor: any;
}
export type IWorkflowExecutionCustomData = {
set(key: string, value: string): void;
setAll(obj: Record<string, string>): void;
get(key: string): string;
getAll(): Record<string, string>;
};
export type IWorkflowDataProxyAdditionalKeys = IDataObject & {
$execution?: {
id: string;
mode: 'test' | 'production';
resumeUrl: string;
resumeFormUrl: string;
customData?: IWorkflowExecutionCustomData;
};
$evaluation?: { runId: string };
$vars?: IDataObject;
$secrets?: IDataObject;
$pageCount?: number;
$tool?: { name: string; parameters: string };
/** @deprecated */
$executionId?: string;
/** @deprecated */
$resumeWebhookUrl?: string;
};
export interface IWorkflowMetadata {
id?: string;
name?: string;
active: boolean;
}
export interface IWebhookResponseData {
workflowData?: INodeExecutionData[][];
webhookResponse?: any;
noWebhookResponse?: boolean;
/** Input the trigger wants connected sub-nodes to receive, also when they run on a worker. */
toolInput?: IDataObject;
}
export type WebhookResponseData = 'allEntries' | 'firstEntryJson' | 'firstEntryBinary' | 'noData';
/**
* Defines how and when response should be sent:
*
* onReceived: Response is sent immidiatly after node done executing
*
* lastNode: Response is sent after the last node finishes executing
*
* responseNode: Response is sent from the Responde to Webhook node
*
* formPage: Special response with executionId sent to the form trigger node
*
* hostedChat: Special response with executionId sent to the hosted chat trigger node
*
* streaming: Response added to runData to httpResponse and streamingEnabled set to true
*/
export type WebhookResponseMode =
| 'onReceived'
| 'lastNode'
| 'responseNode'
| 'formPage'
| 'hostedChat'
| 'streaming';
export interface INodeTypes {
getByName(nodeType: string): INodeType | IVersionedNodeType;
getByNameAndVersion(nodeType: string, version?: number): INodeType;
getKnownTypes(): IDataObject;
}
export type LoadingDetails = {
className: string;
sourcePath: string;
};
export type CredentialLoadingDetails = LoadingDetails & {
supportedNodes?: string[];
extends?: string[];
};
export type NodeLoadingDetails = LoadingDetails;
export type KnownNodesAndCredentials = {
nodes: Record<string, NodeLoadingDetails>;
credentials: Record<string, CredentialLoadingDetails>;
};
export interface LoadedNodesAndCredentials {
nodes: INodeTypeData;
credentials: ICredentialTypeData;
}
export interface LoadedClass<T> {
sourcePath: string;
type: T;
}
type LoadedData<T> = Record<string, LoadedClass<T>>;
export type ICredentialTypeData = LoadedData<ICredentialType>;
export type INodeTypeData = LoadedData<INodeType | IVersionedNodeType>;
/**
* Contract that the runtime consumes from each node source. Implemented by the
* filesystem-backed `DirectoryLoader` in `n8n-core` and in modules
*/
export interface NodeLoader {
packageName: string;
known: KnownNodesAndCredentials;
types: { nodes: INodeTypeDescription[]; credentials: ICredentialType[] };
loadAll(): Promise<void>;
getNode(nodeType: string): LoadedClass<INodeType | IVersionedNodeType>;
getCredential(credentialType: string): LoadedClass<ICredentialType>;
reset(): void;
releaseTypes(): void;
ensureTypesLoaded(): Promise<void>;
resolveSourcePath(sourcePath: string): string;
}
export interface IRun {
data: IRunExecutionData;
/**
* @deprecated Use status instead
*/
finished?: boolean;
mode: WorkflowExecuteMode;
waitTill?: Date | null;
startedAt: Date;
stoppedAt?: Date;
storedAt: ExecutionStorageLocation;
status: ExecutionStatus;
/** ID of the job this execution belongs to. Only in scaling mode. */
jobId?: string;
}
export type SchemaType =
| 'string'
| 'number'
| 'boolean'
| 'bigint'
| 'symbol'
| 'array'
| 'object'
| 'function'
| 'null'
| 'undefined';
export type Schema = { type: SchemaType; key?: string; value: string | Schema[]; path: string };
export interface NodeExecutionSchema {
nodeName: string;
schema: Schema;
}
export interface IRunData {
// node-name: result-data
[key: string]: ITaskData[];
}
export interface ITaskSubRunMetadata {
node: string;
runIndex: number;
}
export interface RelatedExecution {
executionId: string;
workflowId: string;
// In the case of a parent execution, whether the parent should be resumed when the sub execution finishes.
shouldResume?: boolean;
executionContext?: IExecutionContext;
}
/**
* The suspended agent tool call an execution should wake when it finishes — the
* agent-run counterpart to `RelatedExecution`, which wakes a parent workflow.
* Stamped by the agent workflow tool onto the sub-executions it starts.
*/
export interface RelatedAgentRun {
agentId: string;
projectId: string;
/** Agent memory thread the run belongs to; addresses the chat thread to reply in. */
threadId: string;
runId: string;
toolCallId: string;
/** Chat platform the run came from, or `n8n_chat` for the in-app preview. */
integrationType?: string;
/**
* The run started in the in-app preview chat. `integrationType` cannot say
* this: MCP and AI Assistant test runs use `n8n_chat` too, and they must
* resume on the runtime they started on.
*/
previewChat?: boolean;
/** The published n8n Chat channel owns this run. */
publishedN8nChat?: boolean;
/**
* The interactive n8n user, when there is one. The preview chat resumes the draft
* agent version, which gates node and workflow tools by this user's access.
*/
userId?: string;
}
type SubNodeExecutionDataAction = {
nodeName: string;
runIndex: number;
action: EngineRequest['actions'][number];
response?: object;
};
export interface ITaskMetadata {
subRun?: ITaskSubRunMetadata[];
parentExecution?: RelatedExecution;
subExecution?: RelatedExecution;
subExecutionsCount?: number;
/** Sub-executions whose wait parked this execution; read by the sweep that resumes the parent. */
waitingChildExecutionIds?: string[];
/**
* Private-credential usage a sub-execution reported while this execution was
* waiting. The waiting task is popped and the node re-runs disabled on resume,
* so the usage rides on the stack entry to reach the freshly stamped task.
*/
dynamicCredentialsUsage?: DynamicCredentialsUsage;
subNodeExecutionData?: {
actions: SubNodeExecutionDataAction[];
metadata: object;
};
/**
* When true, preserves sourceOverwrite information in pairedItem data during node execution.
* This is used for AI tool nodes to maintain correct expression resolution context, allowing
* tools to access data from nodes earlier in the workflow chain via $() expressions.
*/
preserveSourceOverwrite?: boolean;
preservedSourceOverwrite?: ISourceData;
/**
* Indicates that this node execution is resuming from a previous pause (e.g., AI agent
* resuming after tool execution). When true, the nodeExecuteBefore hook is skipped to
* prevent duplicate event emissions that would cause UI state issues.
* @see AI-1414
*/
nodeWasResumed?: boolean;
/**
* Time saved by this workflow execution in minutes. Used by SavedTime nodes to track
* dynamic time savings that can be calculated based on execution data (e.g., number of
* items processed). The behavior determines how this value interacts with the workflow's
* default timeSavedPerExecution setting.
*/
timeSaved?: {
/** Time saved in minutes */
minutes: number;
};
/**
* Signed URL for resuming form-based waiting executions.
* Contains token for security validation.
*/
resumeFormUrl?: string;
/**
* Signed URL for resuming webhook-based waiting executions.
* Contains token for security validation.
*/
resumeUrl?: string;
/**
* Set when a waiting webhook node is resumed. In that case `data.main` already
* holds the resolved output branches returned by the node's `webhook()` method
* (e.g. `[[], [item], []]`), and the node is flagged as disabled to prevent the
* wait from starting over. The disabled-node handler must then forward every
* output branch instead of only the first one. See `WorkflowExecute.handleDisabledNode`.
*/
forwardAllOutputs?: boolean;
/**
* Error from a sub-workflow that finished with an error while its parent was
* waiting for it. Written onto the parent's Execute Workflow stack entry by
* `updateParentExecutionWithChildResults` (packages/cli) and consumed on resume
* by `WorkflowExecute.runNode`, which fails the node with this error (honoring
* its `onError` setting) instead of letting it pass its input through.
*/
resumeError?: ExecutionError;
/**
* AI model token usage captured from vendor node API responses before the simplify step
* strips it. Used by telemetry to populate ai_input_tokens / ai_output_tokens in node_graph_string.
*/
tokenUsage?: {
inputTokens: number;
outputTokens: number;
};
/**
* Key-value pairs that can be set for tracing - they will be attached to the OTEL node span
* */
tracing?: Record<string, string | number | boolean>;
}
/** The data that gets returned when a node execution starts */
export interface ITaskStartedData {
startTime: number;
/** This index tracks the order in which nodes are executed */
executionIndex: number;
source: Array<ISourceData | null>; // Is an array as nodes have multiple inputs
hints?: NodeExecutionHint[];
}
/** The data that gets returned when a node execution ends */
export interface ITaskData extends ITaskStartedData {
executionTime: number;
executionStatus?: ExecutionStatus;
data?: ITaskDataConnections;
inputOverride?: ITaskDataConnections;
error?: ExecutionError;
/**
* When present, indicates `error` was redacted.
* Contains only non-PII technical metadata from the original error.
*/
redactedError?: IRedactedErrorInfo;
metadata?: ITaskMetadata;
/** True when at least one credential used by this node was resolved dynamically */
usedDynamicCredentials?: boolean;
/**
* True when this node attempted to resolve at least one private (resolvable)
* credential, regardless of whether resolution succeeded. Unlike
* `usedDynamicCredentials` (set only on success and consumed by the redaction
* layer), this is a superset used purely for telemetry: it also captures
* failed attempts (e.g. the running user has not connected the credential).
*/
attemptedDynamicCredentials?: boolean;
}
export interface ISourceData {
previousNode: string;
previousNodeOutput?: number; // If undefined "0" gets used
previousNodeRun?: number; // If undefined "0" gets used
}
export interface StartNodeData {
name: string;
sourceData: ISourceData | null;
}
// The data for all the different kind of connections (like main) and all the indexes
export interface ITaskDataConnections {
// Key for each input type and because there can be multiple inputs of the same type it is an array
// null is also allowed because if we still need data for a later while executing the workflow set temporary to null
// the nodes get as input TaskDataConnections which is identical to this one except that no null is allowed.
[key: string]: Array<INodeExecutionData[] | null>;
}
// Keeps data while workflow gets executed and allows when provided to restart execution
export interface IWaitingForExecution {
// Node name
[key: string]: {
// Run index
[key: number]: ITaskDataConnections;
};
}
export interface ITaskDataConnectionsSource {
// Key for each input type and because there can be multiple inputs of the same type it is an array
// null is also allowed because if we still need data for a later while executing the workflow set temporary to null
// the nodes get as input TaskDataConnections which is identical to this one except that no null is allowed.
[key: string]: Array<ISourceData | null>;
}
export interface IWaitingForExecutionSource {
// Node name
[key: string]: {
// Run index
[key: number]: ITaskDataConnectionsSource;
};
}
export type WorkflowId = IWorkflowBase['id'];
export interface IWorkflowGroup {
id: string;
name: string;
nodeIds: string[];
description?: string;
}
export interface IWorkflowBase {
id: string;
name: string;
description?: string | null;
active: boolean;
isArchived: boolean;
createdAt: Date;
startedAt?: Date;
updatedAt: Date;
nodes: INode[];
connections: IConnections;
settings?: IWorkflowSettings;
staticData?: IDataObject;
pinData?: IPinData;
versionId?: string;
activeVersionId: string | null;
activeVersion?: IWorkflowHistory | null;
versionCounter?: number;
meta?: WorkflowFEMeta;
/** Optional here because IWorkflowBase is used in contexts where node groups
* are irrelevant (executions, telemetry, tests). The DB column is NOT NULL
* with default `[]` and `WorkflowEntity` has this as required. */
nodeGroups?: IWorkflowGroup[];
}
interface IWorkflowHistory {
versionId: string;
workflowId: string;
nodes: INode[];
connections: IConnections;
nodeGroups: IWorkflowGroup[];
authors: string;
name: string | null;
description: string | null;
createdAt: Date;
updatedAt: Date;
}
export interface IWorkflowCredentials {
[credentialType: string]: {
[id: string]: ICredentialsEncrypted;
};
}
export interface IDestinationNode {
nodeName: string;
/**
* Execution mode for the destination node:
* - 'inclusive': Execute up to and including the destination node
* - 'exclusive': Execute up to but excluding the destination node
*/
mode: 'inclusive' | 'exclusive';
}
export type WorkflowExecutionSource = 'user' | 'instance_ai';
export type WorkflowExecutionMockDataSource =
| 'trigger_input'
| 'verification_pin_data'
| 'workflow_pin_data';
export interface IWorkflowExecutionTelemetryMetadata {
mockDataSources?: WorkflowExecutionMockDataSource[];
}
export interface IWorkflowExecutionDataProcess {
destinationNode?: IDestinationNode;
restartExecutionId?: string;
executionMode: WorkflowExecuteMode;
/**
* When true, forces the execution data to be present in the run data.
*/
forceFullExecutionData?: boolean;
/**
* The data that is sent in the body of the webhook that started this
* execution.
*/
executionData?: IRunExecutionData;
runData?: IRunData;
pinData?: IPinData;
retryOf?: string | null;
pushRef?: string;
startNodes?: StartNodeData[];
workflowData: IWorkflowBase;
userId?: string;
projectId?: string;
projectName?: string;
/**
* Who initiated this run. Unset means a regular user-initiated run;
* `'instance_ai'` when the AI assistant ran the workflow on the user's
* behalf. Consumed by the execution push (so the editor can tell agent runs
* apart) and by telemetry.
*/
source?: WorkflowExecutionSource;
/**
* When true, a failure in this run must not dispatch the workflow's error
* workflow (or its own Error Trigger). Set for runs that are a test of the
* workflow rather than a real one, but that still need a production
* execution mode for realistic trigger semantics
*/
suppressErrorWorkflow?: boolean;
telemetryMetadata?: IWorkflowExecutionTelemetryMetadata;
dirtyNodeNames?: string[];
triggerToStartFrom?: {
name: string;
data?: ITaskData;
};
agentRequest?: AiAgentRequest;
httpResponse?: express.Response; // Used for streaming responses
streamingEnabled?: boolean;
/**
* Only engine v2 reads this. The caller mints the data-plane execution id
* when it has to wait for the run's answer, so it can subscribe before the
* run starts.
*/
engineExecutionId?: string;
startedAt?: Date;
// MCP-specific fields for queue mode support
/** Whether this execution was triggered by an MCP tool call. */
isMcpExecution?: boolean;
/** Type of MCP execution: 'service' for MCP Service, 'trigger' for MCP Trigger Node. */
mcpType?: 'service' | 'trigger';
/** MCP session ID for routing responses back to the correct client. */
mcpSessionId?: string;
/** MCP message ID for correlating responses with requests. */
mcpMessageId?: string;
/** Tool call info for MCP Trigger executions in queue mode. */
mcpToolCall?: {
toolName: string;
arguments: Record<string, unknown>;
sourceNodeName?: string;
};
/** The MCP request as node input, so the worker gives the tool node the same `$json` as direct mode. */
mcpToolInput?: IDataObject;
/** Key to uniquely identify this execution, generated by the trigger. */
deduplicationKey?: string;
/** W3C trace context extracted from inbound webhook headers. */
tracingContext?: { traceparent: string; tracestate?: string };
/** Encrypted credential context for a triggered execution. */
encryptedRunnerIdentity?: string;
/** Parent evaluation TestRun.id, exposed to expressions as `$evaluation.runId`. */
evaluationRunId?: string;
}
export interface ExecuteWorkflowOptions {
node?: INode;
parentWorkflowId: string;
inputData?: INodeExecutionData[];
loadedWorkflowData?: IWorkflowBase;
loadedRunData?: IWorkflowExecutionDataProcess;
parentWorkflowSettings?: IWorkflowSettings;
parentCallbackManager?: CallbackManager;
doNotWaitToFinish?: boolean;
parentExecution?: RelatedExecution;
executionMode?: WorkflowExecuteMode;
returnLastRunOnly?: boolean; // Forces the caller to receive only the items from the terminal node's final run.
}
export type AiEvent =
| 'ai-messages-retrieved-from-memory'
| 'ai-message-added-to-memory'
| 'ai-output-parsed'
| 'ai-documents-retrieved'
| 'ai-document-reranked'
| 'ai-document-embedded'
| 'ai-query-embedded'
| 'ai-document-processed'
| 'ai-text-split'
| 'ai-tool-called'
| 'ai-vector-store-searched'
| 'ai-llm-generated-output'
| 'ai-llm-errored'
| 'ai-vector-store-populated'
| 'ai-vector-store-updated';
type AiEventPayload = {
msg: string;
workflowName: string;
executionId: string;
nodeName: string;
workflowId?: string;
nodeType?: string;
};
/** Telemetry emitted when an advanced HITL (human-in-the-loop) response is actioned. */
export type HitlResponseTelemetryPayload = {
nodeType: string;
/** The decision the responder made. */
approved: boolean;
/** Whether the responder was on the node's approver allow-list (empty list = anyone). */
authorized: boolean;
executionId?: string;
workflowId?: string;
};
export type AgentRequestQuery = { [nodeName: string]: Record<string, unknown> | string };
// Used to transport an agent request for partial execution
export interface AiAgentRequest {
query: AgentRequestQuery;
tool: {
name: string;
};
}
export interface IWorkflowExecuteAdditionalData {
credentialsHelper: ICredentialsHelper;
executeWorkflow: (
workflowInfo: IExecuteWorkflowInfo,
additionalData: IWorkflowExecuteAdditionalData,
options: ExecuteWorkflowOptions,
) => Promise<ExecuteWorkflowData>;
executeAgent?: (
source: ExecuteAgentSource,
message: string,
executionId: string,
threadId: string,
additionalData: IWorkflowExecuteAdditionalData,
executionMode: WorkflowExecuteMode,
outputSchema?: JSONSchema7,
workflowContext?: ExecuteAgentWorkflowContext,
invocationContext?: ExecuteAgentInvocationContext,
) => Promise<ExecuteAgentData>;
listAgents?: (userId: string) => Promise<Array<{ id: string; name: string }>>;
getRunExecutionData: (executionId: string) => Promise<IRunExecutionData | undefined>;
executionId?: string;
restartExecutionId?: string;
getRuntimeCredential(
runExecutionData: IRunExecutionData,
alias: string,
): Promise<IDataObject[string] | undefined>;
/**
* Backing implementations for the trigger identity pipeline exposed to nodes on
* `IWebhookFunctions` (see the docs there). Optional here because they are only
* wired in by the CLI webhook layer when the OAuth server is available; the
* context methods throw if a node reaches them while unset.
*/
beginN8nOAuth2Flow?: (resourceUrl: string, metadata?: Record<string, string>) => Promise<string>;
completeN8nOAuth2Flow?: (code: string, state: string) => Promise<N8nOAuth2FlowResult>;
refreshN8nOAuth2Flow?: (
refreshToken: string,
resourceUrl: string,
) => Promise<N8nOAuth2RefreshResult>;
validateN8nOAuth2Token?: (
token: string,
resourceUrl: string,
) => Promise<N8nOAuth2ValidationResult>;
establishTriggerIdentity?(token: string, resource: string, subject?: string): Promise<void>;
checkTriggerCredentialStatus?(): Promise<CredentialCheckResult | undefined>;
currentNodeExecutionIndex: number;
httpResponse?: express.Response;
httpRequest?: express.Request;
streamingEnabled?: boolean;
restApiUrl: string;
instanceBaseUrl: string;
setExecutionStatus?: (status: ExecutionStatus) => void;
sendDataToUI?: (type: string, data: IDataObject | IDataObject[]) => void;
formBaseUrl: string;
formWaitingBaseUrl: string;
formTestBaseUrl: string;
webhookBaseUrl: string;
webhookWaitingBaseUrl: string;
webhookTestBaseUrl: string;
mcpBaseUrl: string;
mcpTestBaseUrl: string;
currentNodeParameters?: INodeParameters;
executionTimeoutTimestamp?: number;
userId?: string;
workflowId?: string;
projectId?: string;
variables: IDataObject;
logAiEvent: (eventName: AiEvent, payload: AiEventPayload) => void;
logHitlResponse?: (payload: HitlResponseTelemetryPayload) => void;
parentCallbackManager?: CallbackManager;
/**
* The execution mode of the root (top-level) workflow. Used to propagate manual
* mode context into subworkflows so credential resolution can fall back to static
* data consistently across the entire execution tree.
*/
rootExecutionMode?: WorkflowExecuteMode;
/** Parent evaluation TestRun.id, exposed to expressions as `$evaluation.runId`. */
evaluationRunId?: string;
startRunnerTask<T, E = unknown>(
additionalData: IWorkflowExecuteAdditionalData,
jobType: string,
settings: unknown,
executeFunctions: IExecuteFunctions,
inputData: ITaskDataConnections,
node: INode,
workflow: Workflow,
runExecutionData: IRunExecutionData,
runIndex: number,
itemIndex: number,
activeNodeName: string,
connectionInputData: INodeExecutionData[],
siblingParameters: INodeParameters,
mode: WorkflowExecuteMode,
envProviderState: EnvProviderState,
executeData?: IExecuteData,
): Promise<Result<T, E>>;
getRunnerStatus?(taskType: string): { available: true } | { available: false; reason?: string };
validateCookieAuth?: (cookieValue: string) => Promise<IUser>;
getUserById?: (id: string) => Promise<IUser | undefined>;
/**
* Mutable flag set to true during a node's execution if any credential was resolved
* dynamically. Reset to false by the execution engine before each node runs.
*/
currentNodeUsedDynamicCredentials?: boolean;
/**
* Mutable flag set to true during a node's execution when it attempts to resolve a
* private (resolvable) credential, set before resolution is attempted so it is
* recorded even when resolution throws (e.g. the user has not connected the
* credential). Reset to false by the execution engine before each node runs.
* Telemetry-only; the redaction layer relies on `currentNodeUsedDynamicCredentials`.
*/
currentNodeAttemptedDynamicCredentials?: boolean;
/**
* The n8n user a dynamically-resolved private credential belongs to, set during
* credential resolution when the resolver maps the execution's identity to an n8n
* user. Execution-scoped (one identity per execution), so it is not reset per node.
* The execution engine copies it onto `runtimeData.executedByUserId` for the
* redaction layer.
*/
dynamicCredentialsResolvedUserId?: string;
otel?: {
injectTraceHeaders: (
executionId: string,
nodeName: string | undefined,
headers: Record<string, string>,
) => void;
};
}
export type WorkflowActivateMode =
| 'init'
| 'create' // unused
| 'update'
| 'activate'
| 'manual' // unused
| 'leadershipChange';
export namespace WorkflowSettings {
export type CallerPolicy = 'any' | 'none' | 'workflowsFromAList' | 'workflowsFromSameOwner';
export type SaveDataExecution = 'DEFAULT' | 'all' | 'none';
export type RedactionPolicy = 'none' | 'all' | 'non-manual' | 'manual-only';
}
export type WorkflowSettingsBinaryMode = typeof BINARY_MODE_SEPARATE | typeof BINARY_MODE_COMBINED;
export interface IWorkflowSettings {
timezone?: 'DEFAULT' | string;
errorWorkflow?: 'DEFAULT' | string;
callerIds?: string;
callerPolicy?: WorkflowSettings.CallerPolicy;
saveDataErrorExecution?: WorkflowSettings.SaveDataExecution;
saveDataSuccessExecution?: WorkflowSettings.SaveDataExecution;
saveManualExecutions?: 'DEFAULT' | boolean;
saveExecutionProgress?: 'DEFAULT' | boolean;
executionTimeout?: number;
executionOrder?: 'v0' | 'v1';
engineType?: 'v1' | 'v2';
binaryMode?: WorkflowSettingsBinaryMode;
timeSavedPerExecution?: number;
timeSavedMode?: 'fixed' | 'dynamic';
availableInMCP?: boolean;
credentialResolverId?: string;
redactionPolicy?: WorkflowSettings.RedactionPolicy;
customTelemetryTags?: ICustomTelemetryTag[];
}
export interface WorkflowFEMeta {
onboardingId?: string;
templateId?: string;
instanceId?: string;
templateCredsSetupCompleted?: boolean;
}
export interface WorkflowTestData {
description: string;
input: {
workflowData: {
nodes: INode[];
connections: IConnections;
settings?: IWorkflowSettings;
};
};
output: {
assertBinaryData?: boolean;
nodeExecutionOrder?: string[];
nodeExecutionStack?: IExecuteData[];
testAllOutputs?: boolean;
nodeData: {
[key: string]: any[][];
};
error?: string;
};
nock?: {
baseUrl: string;
mocks: Array<{
method: 'delete' | 'get' | 'patch' | 'post' | 'put';
path: string;
requestBody?: RequestBodyMatcher;
requestHeaders?: Record<string, RequestHeaderMatcher>;
statusCode: number;
responseBody: string | object;
responseHeaders?: ReplyHeaders;
}>;
};
trigger?: {
mode: WorkflowExecuteMode;
input: INodeExecutionData;
};
credentials?: Record<string, ICredentialDataDecryptedObject>;
}
export type LogLevel = (typeof LOG_LEVELS)[number];
export type LogMetadata = {
[key: string]: unknown;
scopes?: LogScope[];
file?: string;
function?: string;
};
export type Logger = Record<
Exclude<LogLevel, 'silent'>,
(message: string, metadata?: LogMetadata) => void
>;
export type LogLocationMetadata = Pick<LogMetadata, 'file' | 'function'>;
export interface IStatusCodeMessages {
[key: string]: string;
}
export type DocumentationLink = {
url: string;
};
export type CodexData = {
categories?: string[];
subcategories?: { [category: string]: string[] };
resources?: {
credentialDocumentation?: DocumentationLink[];
primaryDocumentation?: DocumentationLink[];
};
alias?: string[];
};
export type JsonValue = string | number | boolean | null | JsonObject | JsonValue[];
export type JsonObject = { [key: string]: JsonValue };
export type AllEntities<M> = M extends { [key: string]: string } ? Entity<M, keyof M> : never;
export type Entity<M, K> = K extends keyof M ? { resource: K; operation: M[K] } : never;
export type PropertiesOf<M extends { resource: string; operation: string }> = Array<
Omit<INodeProperties, 'displayOptions'> & {
displayOptions?: {
[key in 'show' | 'hide']?: {
resource?: Array<M['resource']>;
operation?: Array<M['operation']>;
[otherKey: string]: Array<NodeParameterValue | DisplayCondition> | undefined;
};
};
}
>;
// Telemetry
export interface ITelemetryTrackProperties {
user_id?: string;
[key: string]: GenericValue;
}
export interface INodesGraph {
node_types: string[];
node_connections: IDataObject[];
nodes: INodesGraphNode;
notes: INotesGraphNode;
is_pinned: boolean;
}
export interface INodesGraphNode {
[key: string]: INodeGraphItem;
}
export interface INotesGraphNode {
[key: string]: INoteGraphItem;
}
export interface INoteGraphItem {
overlapping: boolean;
position: [number, number];
height: number;
width: number;
}
export interface INodeGraphItem {
id: string;
type: string;
version?: number;
resource?: string;
operation?: string;
domain?: string; // HTTP Request node v1
domain_base?: string; // HTTP Request node v2
position: [number, number];
mode?: string;
credential_type?: string; // HTTP Request node v2
credential_set?: boolean; // HTTP Request node v2
method?: string; // HTTP Request node v2
src_node_id?: string;
src_instance_id?: string;
agent?: string; //@n8n/n8n-nodes-langchain.agent
is_streaming?: boolean; //@n8n/n8n-nodes-langchain.agent
prompts?: IDataObject[] | IDataObject; //ai node's prompts, cloud only
use_responses_api?: boolean; //@n8n/n8n-nodes-langchain.lmChatOpenAi
toolSettings?: IDataObject; //various langchain tool's settings
sql?: string; //merge node combineBySql, cloud only
workflow_id?: string; //@n8n/n8n-nodes-langchain.toolWorkflow and n8n-nodes-base.executeWorkflow
response_mode?: string; // @n8n/n8n-nodes-langchain.chatTrigger, n8n-nodes-base.webhook selected response mode
public_chat?: boolean; // @n8n/n8n-nodes-langchain.chatTrigger
runs?: number;
items_total?: number;
metric_names?: string[];
language?: string; // only for Code node: 'javascript' or 'python' or 'pythonNative'
package_version?: string; // only for community nodes
used_guardrails?: string[]; // only for @n8n/n8n-nodes-langchain.guardrails
mcp_client_auth_method?: string; // for @n8n/n8n-nodes-langchain.mcpClientTool and @n8n/n8n-nodes-langchain.mcpClient
mcp_server_domain_base?: string; // registrable domain of MCP server URL, for mcpClientTool / mcpClient
ai_model?: string; // AI model for model nodes and standalone AI nodes
ai_input_tokens?: number; // AI input (prompt) tokens for model nodes
ai_output_tokens?: number; // AI output (completion) tokens for model nodes
ai_gateway_credentials?: boolean; // true if node used n8n Connect / AI Gateway managed credentials
}
export interface INodeNameIndex {
[name: string]: string;
}
export interface INodesGraphResult {
nodeGraph: INodesGraph;
nameIndices: INodeNameIndex;
webhookNodeNames: string[];
evaluationTriggerNodeNames: string[];
}
export interface FeatureFlags {
[featureFlag: string]: string | boolean | undefined;
}
export interface FeatureFlagPayloads {
[featureFlag: string]: JsonValue;
}
export interface IConnectedNode {
name: string;
indicies: number[];
depth: number;
}
export type PublicInstalledPackage = {
packageName: string;
installedVersion: string;
authorName?: string;
authorEmail?: string;
installedNodes: PublicInstalledNode[];
createdAt: Date;
updatedAt: Date;
updateAvailable?: string;
failedLoading?: boolean;
};
export type PublicInstalledNode = {
name: string;
type: string;
latestVersion: number;
package: PublicInstalledPackage;
};
export interface NodeExecutionWithMetadata extends INodeExecutionData {
pairedItem: IPairedItemData | IPairedItemData[];
}
export type AnnotationVote = 'up' | 'down';
export interface ExecutionSummary {
id: string;
/**
* @deprecated Use status instead
*/
finished?: boolean;
mode: WorkflowExecuteMode;
retryOf?: string | null;
retrySuccessId?: string | null;
waitTill?: Date;
createdAt: Date;
startedAt: Date | null;
stoppedAt?: Date;
workflowId: string;
workflowName?: string;
workflowVersionId?: string | null;
jsonSizeBytes?: number;
binaryDataSizeBytes?: number;
status: ExecutionStatus;
lastNodeExecuted?: string;
executionError?: ExecutionError;
nodeExecutionStatus?: {
[key: string]: IExecutionSummaryNodeExecutionResult;
};
usedPrivateCredentials?: boolean;
annotation?: {
vote?: AnnotationVote | null;
tags: Array<{
id: string;
name: string;
}>;
};
}
export interface IExecutionSummaryNodeExecutionResult {
executionStatus: ExecutionStatus;
errors?: Array<{
name?: string;
message?: string;
description?: string;
}>;
}
export interface ResourceMapperFields {
fields: ResourceMapperField[];
emptyFieldsNotice?: string;
}
export interface WorkflowInputsData {
fields: ResourceMapperField[];
dataMode: string;
subworkflowInfo?: { workflowId?: string; triggerId?: string };
}
export interface ResourceMapperField {
/** Field key sent to/received from the API (e.g. "name", "email"). Becomes the key in the mapped value object at runtime. */
id: string;
/** Human-readable label shown in the mapping form. */
displayName: string;
/** When true the field is pre-selected as a matching column for update/upsert. If only one field exists in the schema it becomes the match regardless. */
defaultMatch: boolean;
/** Allows the field to appear in the "Matching Columns" dropdown. Set false for fields that can never uniquely identify a record. */
canBeUsedToMatch?: boolean;
/** When true the field cannot be removed in 'add' mode and is labelled "mandatory field". Fields hidden by addAllFields=false are still shown when required=true. */
required: boolean;
/** When false the field is completely excluded from the UI, the "Add field" dropdown, and matching selectors. Use for computed/read-only fields the user must never touch. */
display: boolean;
/** Controls the input widget: 'options' → dropdown (requires `options`), 'boolean' → toggle, 'number' → numeric, 'dateTime' → date picker, 'array'/'object' → JSON editor. */
type?: FieldType;
/** When true the field is hidden by default but still accessible via the "Add field" dropdown so the user can opt in. Toggled by user add/remove actions. */
removed?: boolean;
/** Enumerated choices for fields of type 'options'. Each entry: { name (label), value }. */
options?: INodePropertyOptions[];
/** Disables the input so the user cannot edit the value. Matching columns are always forced read-only by the UI regardless of this flag. */
readOnly?: boolean;
/** Pre-filled value applied when the field is first shown or re-added. Omit to leave the input empty. */
defaultValue?: string | number | boolean | null;
}
export type FormFieldsParameter = Array<{
fieldLabel: string;
elementName?: string;
fieldType?: string;
requiredField?: boolean;
fieldOptions?: { values: Array<{ option: string }> };
multiselect?: boolean;
multipleFiles?: boolean;
acceptFileTypes?: string;
formatDate?: string;
html?: string;
placeholder?: string;
defaultValue?: string;
fieldName?: string;
fieldValue?: string;
limitSelection?: 'exact' | 'range' | 'unlimited';
numberOfSelections?: number;
minSelections?: number;
maxSelections?: number;
}>;
export type FieldTypeMap = {
// eslint-disable-next-line id-denylist
boolean: boolean;
// eslint-disable-next-line id-denylist
number: number;
// eslint-disable-next-line id-denylist
string: string;
'string-alphanumeric': string;
dateTime: string;
time: string;
array: unknown[];
object: object;
options: any;
url: string;
jwt: string;
'form-fields': FormFieldsParameter;
binary: string;
};
export type FieldType = keyof FieldTypeMap;
export type ValidationResult<T extends FieldType = FieldType> =
| { valid: false; errorMessage: string }
| {
valid: true;
newValue?: FieldTypeMap[T];
};
export type ResourceMapperValue = {
mappingMode: string;
value: { [key: string]: string | number | boolean | null } | null;
matchingColumns: string[];
schema: ResourceMapperField[];
attemptToConvertTypes: boolean;
convertFieldsToString: boolean;
};
export type FilterOperatorType =
| 'string'
| 'number'
| 'boolean'
| 'array'
| 'object'
| 'dateTime'
| 'any';
export interface FilterOperatorValue {
type: FilterOperatorType;
operation: string;
rightType?: FilterOperatorType;
singleValue?: boolean; // default = false
}
export type FilterConditionValue = {
id: string;
leftValue: NodeParameterValue | NodeParameterValue[];
operator: FilterOperatorValue;
rightValue: NodeParameterValue | NodeParameterValue[];
};
export type FilterOptionsValue = {
caseSensitive: boolean;
leftValue: string;
typeValidation: 'strict' | 'loose';
version: 1 | 2 | 3;
};
export type FilterValue = {
options: FilterOptionsValue;
conditions: FilterConditionValue[];
combinator: FilterTypeCombinator;
};
export type AssignmentCollectionValue = {
assignments: AssignmentValue[];
};
export type AssignmentValue = {
id: string;
name: string;
value: NodeParameterValue;
type?: string;
};
export interface ExecutionOptions {
limit?: number;
}
export interface ExecutionFilters {
/**
* @deprecated Use status instead
*/
finished?: boolean;
mode?: WorkflowExecuteMode[];
retryOf?: string;
retrySuccessId?: string;
status?: ExecutionStatus[];
waitTill?: boolean;
workflowId?: number | string;
}
export type NpsSurveyRespondedState = { lastShownAt: number; responded: true };
export type NpsSurveyWaitingState = {
lastShownAt: number;
waitingForResponse: true;
ignoredCount: number;
};
export type NpsSurveyState = NpsSurveyRespondedState | NpsSurveyWaitingState;
export interface IUserSettings {
isOnboarded?: boolean;
firstSuccessfulWorkflowId?: string;
userActivated?: boolean;
userActivatedAt?: number;
allowSSOManualLogin?: boolean;
npsSurvey?: NpsSurveyState;
easyAIWorkflowOnboarded?: boolean;
userClaimedAiCredits?: boolean;
dismissedCallouts?: Record<string, boolean>;
instanceAi?: {
credentialId?: string | null;
modelName?: string;
localGatewayDisabled?: boolean;
};
mcpJsonNudge?: {
impressions: number;
};
}
export interface IProcessedDataConfig {
availableModes: string;
mode: string;
}
export interface IDataDeduplicator {
checkProcessedAndRecord(
items: DeduplicationItemTypes[],
context: DeduplicationScope,
contextData: ICheckProcessedContextData,
options: ICheckProcessedOptions,
): Promise<IDeduplicationOutput>;
removeProcessed(
items: DeduplicationItemTypes[],
context: DeduplicationScope,
contextData: ICheckProcessedContextData,
options: ICheckProcessedOptions,
): Promise<void>;
clearAllProcessedItems(
context: DeduplicationScope,
contextData: ICheckProcessedContextData,
options: ICheckProcessedOptions,
): Promise<void>;
getProcessedDataCount(
context: DeduplicationScope,
contextData: ICheckProcessedContextData,
options: ICheckProcessedOptions,
): Promise<number>;
}
export interface ICheckProcessedContextData {
node?: INode;
workflow: {
id: string;
active: boolean;
};
}
export type N8nAIProviderType = 'openai' | 'unknown';
export type Functionality = 'regular' | 'configuration-node' | 'pairedItem';
export type CallbackManager = CallbackManagerLC;
export type IPersonalizationSurveyAnswersV4 = {
version: 'v4';
personalization_survey_submitted_at: string;
personalization_survey_n8n_version: string;
automationGoalDevops?: string[] | null;
automationGoalDevopsOther?: string | null;
companyIndustryExtended?: string[] | null;
otherCompanyIndustryExtended?: string[] | null;
companySize?: string | null;
companyType?: string | null;
automationGoalSm?: string[] | null;
automationGoalSmOther?: string | null;
usageModes?: string[] | null;
email?: string | null;
role?: string | null;
roleOther?: string | null;
reportedSource?: string | null;
reportedSourceOther?: string | null;
};
export type ChunkType = 'begin' | 'item' | 'end' | 'error';
export interface StructuredChunk {
type: ChunkType;
content?: string;
metadata: {
nodeId: string;
nodeName: string;
runIndex: number;
itemIndex: number;
timestamp: number;
};
}
export type ApiKeyAudience = 'public-api' | 'mcp-server-api';