{
  "generator": {
    "name": "Symvanta",
    "url": "https://symvanta.com"
  },
  "page": {
    "url": "https://symvanta.com/architecture/n8n",
    "title": "n8n Architecture: How It Actually Works",
    "description": "How the n8n codebase is structured: module map, load-bearing symbols, and dependency structure, generated by Symvanta.",
    "datePublished": "2026-08-25",
    "dateModified": "2026-08-25"
  },
  "repository": {
    "name": "n8n-io/n8n",
    "url": "https://github.com/n8n-io/n8n",
    "commit": "af27b0d",
    "license": "Sustainable Use License"
  },
  "graph": {
    "modules": 778,
    "dependencyCycles": 255,
    "modularityQ": 0.93,
    "largestCycleFiles": 269,
    "mutuallyRecursiveGroups": 85
  },
  "summaryMarkdown": "n8n is a workflow automation server. A Vue editor draws the workflow, a REST and\nwebhook server stores it, an execution engine walks it node by node, and a large\nlibrary of integration nodes makes the outbound calls. Symvanta's graph of the\nrepo at `af27b0d` on `master` detects\n778 functional modules (modularity Q=0.93), and serves the 500 largest, which\nhold 75,637 symbols.\n\nThe mass of the codebase is integrations. 197 of those 500 modules live in\n`packages/nodes-base` or `packages/@n8n/nodes-langchain`, roughly one per vendor\ntransport, and five of the twelve repo-wide load-bearing functions are a vendor's\nown HTTP helper: TheHive's, Google's, OpenAI's, Microsoft Excel's and Pipedrive's.\nThat is what an integration catalog looks like in a call graph, and this one holds\n308 vendor directories and 406 credential definition files at this commit.\nThose 197 modules are 17.9% of the mapped symbols, behind `packages/cli` at 24.8%\nand the four AI assistant packages at 19.3%, ahead of the editor frontend at\n10.8%. The execution engine is genuinely small and deliberately so: the clusters\nwhose members sit in `packages/core/src/execution-engine`, `packages/workflow` and\n`packages/cli/src/scaling` are 6.3% of the map between them. The engine walks the\ngraph and hands each node an execution context, and the work happens inside the\nnode.\n\nModule names on this page are read by hand from the packages the members live in\nand from each hub symbol's file. The generated cluster summaries are used for\nnothing here, because on this repo several of them are wrong: the cluster that\nholds the execution engine was summarized as logging, and the cluster that holds\nthe REST controllers was summarized as licensing. Every cluster is cited by its\nid, the member-set hash that survives a reindex. Symbol counts, hubs, ids and\nedge weights are what the graph computed.",
  "subsystems": [
    {
      "name": "Instance AI assistant runtime",
      "descriptionMarkdown": "Module id `1122f926faac11ce`, 3,706 symbols, the largest on the map. Its members live in `packages/@n8n/instance-ai/src` and its hub is `InstanceAiContext` from `src/types.ts`, the context object every assistant tool receives. `InstanceAiToolRegistry`, `OrchestrationContext`, `WorkflowBuildOutcome` and `ModelConfig` sit beside it. Its heaviest outgoing edge is 148 calls into the editor's agents feature, which is the other half of the same product surface."
    },
    {
      "name": "Node errors and credential helpers",
      "descriptionMarkdown": "Module id `532185b8a99ea6b4`, 3,337 symbols. The hub is `NodeApiError` from `packages/workflow/src/errors/node-api.error.ts`, and the members are `NodeOperationError` plus a wide slice of the credential and transport helpers under `packages/nodes-base` (`beeminderApiRequest`, `getActiveCredentialType`, `getHost`, `DatabricksCredentials`). Integration nodes raise these two error types when a request fails, so the error classes cluster with the transports that throw them instead of with the rest of the workflow package."
    },
    {
      "name": "Agents SDK types and JSON values",
      "descriptionMarkdown": "Module id `4e0cee763e689c87`, 3,183 symbols, hubbed on `JSONValue` from `packages/@n8n/agents/src/types/utils/json.ts`. This is the type surface of the agents SDK: `JSONObject`, `JSONArray`, `AgentMessage`, `AgentDbMessage`, `BuiltTool`. A one-line recursive type alias ranks as the hub here because every message and tool definition in the SDK is built out of it."
    },
    {
      "name": "Editor UI composables and log view",
      "descriptionMarkdown": "Module id `f7c4c628d73c66c6`, 2,887 symbols, members under `packages/frontend/editor-ui/src/app/composables`: `useWorkflowDocumentStore`, `useTelemetry`, and the `LogEntry` and `NodeLogEntry` shapes the execution log view renders. This is one of the two clusters whose hub is an ambient declaration, so the name comes from where the members live."
    },
    {
      "name": "REST controllers and response errors",
      "descriptionMarkdown": "Module id `ad43c0ce9514e986`, 2,871 symbols, hubbed on `ResponseError` from `packages/cli/src/errors/response-errors/abstract/response.error.ts` with `BadRequestError` and `ForbiddenError` under it. Around them sit the chat hub module (`ChatHubSession`, `ChatHubTool`) and the license accessors (`License.manager`, `License.isLicensed`). Controllers throw response errors and check the license on the same request path, so the graph puts them together. Its heaviest edges are 116 calls into the server agents module and 102 into the database entities."
    },
    {
      "name": "Server agents and the editor agents feature",
      "descriptionMarkdown": "The server side is module id `1fbd1c236a830671`, 2,185 symbols under `packages/cli/src/modules/agents`, hubbed on the `Agent` entity in `entities/agent.entity.ts`, with `AgentHistory`, `AgentExecution`, `AgentExecutionThread` and `AgentRepository`. The editor side is module id `43d35099e877cd82`, 2,541 symbols under `packages/frontend/editor-ui/src/features/agents/composables`. The server module takes 116 calls from the REST controller cluster and sends 46 back."
    },
    {
      "name": "Execution engine and queue runner",
      "descriptionMarkdown": "The engine is two modules. Id `83094e44dd365222` (1,255 symbols) has its members in `packages/core/src/execution-engine` while its hub is `Logger.scoped` from `packages/@n8n/backend-common`, which is why its generated summary described logging. Id `8d9cdb44e96f26ba` (540 symbols) is the partial-execution machinery: `DirectedGraph`, `GraphConnection`, `NodeExecutionContext`, `Workflow.getNode`. The queue runner is another two: id `c4a90fa06ac89e11` (616 symbols) under `packages/cli/src/scaling`, holding `ExecutionRef`, `ActiveExecutions` and the execution lifecycle hook handlers, and id `e4d215ec66e79a25` (399 symbols) for the pub/sub commands workers exchange. Together with `packages/workflow`, that is 6.3% of the mapped symbols."
    },
    {
      "name": "Integration transports",
      "descriptionMarkdown": "The 197 integration modules are the long tail of the map, each one a vendor's transport file plus the actions that call through it, 13,575 symbols in total. Past the error cluster above, the largest are id `a1594a6836dd063d` (787 symbols) hubbed on `googleApiRequest` from the Gmail helpers, id `be2b32f6e5a912ef` (660) on `pipedriveApiRequest`, id `f6c860f8c23c0180` (630) on `getGoogleAccessToken`, and id `4419e5cfc5c6b81d` (440) on the shared `verifySignature` webhook helper. The pattern repeats verbatim down the tail: one `*ApiRequest` function per vendor, a credentials type, and a directory of actions that call it. Symvanta's clustering gives each of those its own module, which is why 197 of the 500 served modules are integrations while they are only 17.9% of the symbols."
    },
    {
      "name": "Workflow error hierarchy",
      "descriptionMarkdown": "Module id `91ba1b8bb399e612`, 1,891 symbols in `packages/workflow/src`: `BaseError`, `NodeError`, `ExecutionBaseError`, the `JsonObject` and `JsonValue` types, and helpers like `removeCircularRefs`. This is the package every other package imports its error classes from."
    },
    {
      "name": "Evaluation harnesses",
      "descriptionMarkdown": "Two of the ten drawn clusters are test harnesses for the AI features: id `ca1dbdc7a8b683df` (2,650 symbols, `packages/@n8n/instance-ai/evaluations`, hubbed on the harness `N8nClient`) and id `20557a094fd53329` (2,082 symbols, `packages/@n8n/ai-workflow-builder.ee/evaluations`). With the rest of `packages/testing` they are 9.8% of the mapped symbols. They rank this high because these harnesses drive a real n8n instance, so they carry client, workflow and validation code of their own."
    }
  ],
  "startReading": {
    "symbols": [
      {
        "name": "isRecord",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/@n8n/utils/src/is-record.ts#L1"
      },
      {
        "name": "Logger.scoped",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/@n8n/backend-common/src/logging/logger.ts#L76"
      },
      {
        "name": "makeRestApiRequest",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/frontend/@n8n/rest-api-client/src/utils.ts#L196"
      },
      {
        "name": "Telemetry.track",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/telemetry/index.ts#L580"
      },
      {
        "name": "MessageEventBus.sendAuditEvent",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/eventbus/message-event-bus/message-event-bus.ts#L251"
      },
      {
        "name": "theHiveApiRequest",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/nodes-base/nodes/TheHiveProject/transport/requestApi.ts#L10"
      },
      {
        "name": "LicenseState.isLicensed",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/@n8n/backend-common/src/license-state.ts#L33"
      },
      {
        "name": "JsonColumn",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/@n8n/db/src/entities/abstract-entity.ts#L30"
      },
      {
        "name": "getGoogleAccessToken",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/nodes-base/nodes/Google/GenericFunctions.ts#L75"
      },
      {
        "name": "apiRequest",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/@n8n/nodes-langchain/nodes/vendors/OpenAi/transport/index.ts#L15"
      },
      {
        "name": "microsoftApiRequest",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/nodes-base/nodes/Microsoft/Excel/v2/transport/index.ts#L114"
      },
      {
        "name": "pipedriveApiRequest",
        "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/nodes-base/nodes/Pipedrive/v2/transport/pipedrive.api.ts#L30"
      }
    ],
    "endpoints": []
  },
  "requestFlow": [
    {
      "position": 1,
      "symbol": "WorkflowRunner.run",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/workflow-runner.ts#L242",
      "markdown": "`WorkflowRunner.run` ([`packages/cli/src/workflow-runner.ts:242`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/workflow-runner.ts#L242)) is the entry point 13 files in the server call: retries, waiting executions resumed by the wait tracker, chat runs, the MCP execute and test tools, and the evaluation runner."
    },
    {
      "position": 2,
      "symbol": "CredentialsPermissionChecker.check",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/executions/pre-execution-checks/credentials-permission-checker.ts#L102",
      "markdown": "`CredentialsPermissionChecker.check` ([`packages/cli/src/executions/pre-execution-checks/credentials-permission-checker.ts:102`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/executions/pre-execution-checks/credentials-permission-checker.ts#L102)) maps every credential id the workflow's nodes use to the projects the workflow belongs to, and refuses the run when one of them is not shared with any of those projects."
    },
    {
      "position": 3,
      "symbol": "ActiveExecutions.add",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/active-executions.ts#L62",
      "markdown": "`ActiveExecutions.add` ([`packages/cli/src/active-executions.ts:62`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/active-executions.ts#L62)) reserves a concurrency slot, creates the execution row at status `new`, and builds the in-memory record everything later attaches to: the cancellation controller, the response promise, the post-execute promise."
    },
    {
      "position": 4,
      "symbol": "WorkflowRunner.enqueueExecution",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/workflow-runner.ts#L515",
      "markdown": "`WorkflowRunner.enqueueExecution` ([`packages/cli/src/workflow-runner.ts:515`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/workflow-runner.ts#L515)) is the queue-mode branch: it hands the job to [`ScalingService.addJob`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/scaling/scaling.service.ts#L225) and waits for a worker to report the result. The worker picks it up in [`JobProcessor.processJob`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/scaling/job-processor.ts#L84) and runs step 9 onwards in its own process."
    },
    {
      "position": 5,
      "symbol": "WorkflowRunner.runMainProcess",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/workflow-runner.ts#L343",
      "markdown": "`WorkflowRunner.runMainProcess` ([`packages/cli/src/workflow-runner.ts:343`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/workflow-runner.ts#L343)) is the other branch, running the workflow in the server process. It registers the lifecycle hooks, arms the execution timeout, and owns the failure paths."
    },
    {
      "position": 6,
      "symbol": "ExecutionRepository.setRunning",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/@n8n/db/src/repositories/execution.repository.ts#L380",
      "markdown": "`ExecutionRepository.setRunning` ([`packages/@n8n/db/src/repositories/execution.repository.ts:380`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/@n8n/db/src/repositories/execution.repository.ts#L380)) flips the row to `running` inside a transaction and keeps the original `startedAt` when this is a resumed execution."
    },
    {
      "position": 7,
      "symbol": "ManualExecutionService.runManually",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/manual-execution.service.ts#L49",
      "markdown": "`ManualExecutionService.runManually` ([`packages/cli/src/manual-execution.service.ts:49`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/manual-execution.service.ts#L49)) decides what a manual run actually executes: the whole workflow from its trigger, or a partial re-run seeded with the pinned data and start nodes the editor sent."
    },
    {
      "position": 8,
      "symbol": "DirectedGraph.fromWorkflow",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/partial-execution-utils/directed-graph.ts#L466",
      "markdown": "`DirectedGraph.fromWorkflow` ([`packages/core/src/execution-engine/partial-execution-utils/directed-graph.ts:466`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/partial-execution-utils/directed-graph.ts#L466)) turns the stored workflow into the graph object the partial-execution logic walks to find which nodes a re-run has to touch."
    },
    {
      "position": 9,
      "symbol": "WorkflowExecute.processRunExecutionData",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/workflow-execute.ts#L1586",
      "markdown": "`WorkflowExecute.processRunExecutionData` ([`packages/core/src/execution-engine/workflow-execute.ts:1586`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/workflow-execute.ts#L1586)) is the loop, roughly 970 lines of it. It shifts the next node off the execution stack, checks that its input data is ready, runs it, pushes the nodes its output feeds, and repeats until the stack is empty. Per-node retries and the wait state live inside the same loop."
    },
    {
      "position": 10,
      "symbol": "WorkflowExecute.runNode",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/workflow-execute.ts#L1311",
      "markdown": "`WorkflowExecute.runNode` ([`packages/core/src/execution-engine/workflow-execute.ts:1311`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/workflow-execute.ts#L1311)) handles one node: disabled nodes, execute-once, trigger and poll nodes, and the error-output branch that lets a workflow continue past a failure."
    },
    {
      "position": 11,
      "symbol": "WorkflowExecute.executeNode",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/workflow-execute.ts#L1050",
      "markdown": "`WorkflowExecute.executeNode` ([`packages/core/src/execution-engine/workflow-execute.ts:1050`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/workflow-execute.ts#L1050)) builds an [`ExecuteContext`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/node-execution-context/execute-context.ts#L43) and calls [`INodeType.execute`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/workflow/src/interfaces.ts#L2433). This one edge is the boundary between the engine and the integration library: above it is n8n's code, below it is the node's, and the 197 transport modules hang off the far side."
    },
    {
      "position": 12,
      "symbol": "ExecutionLifecycleHooks.runHook",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/execution-lifecycle-hooks.ts#L118",
      "markdown": "`ExecutionLifecycleHooks.runHook` ([`packages/core/src/execution-engine/execution-lifecycle-hooks.ts:118`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/core/src/execution-engine/execution-lifecycle-hooks.ts#L118)) runs the handlers registered for one of eight events (`workflowExecuteBefore`, `nodeExecuteBefore`, `nodeExecuteAfter`, `nodeFetchedData`, `sendResponse`, `sendChunk`, `workflowExecuteResume`, `workflowExecuteAfter`). Saving progress, pushing status to the editor and recording statistics all hang here."
    },
    {
      "position": 13,
      "symbol": "ActiveExecutions.finalizeExecution",
      "sourceUrl": "https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/active-executions.ts#L246",
      "markdown": "`ActiveExecutions.finalizeExecution` ([`packages/cli/src/active-executions.ts:246`](https://github.com/n8n-io/n8n/blob/af27b0d3d6b3603e5a41529d687b1b15aab570de/packages/cli/src/active-executions.ts#L246)) closes a streaming response if the run had one, resolves the post-execute promise, and drops the in-memory record."
    }
  ],
  "diagrams": [
    {
      "url": "https://symvanta.com/architecture/n8n/module-map.svg",
      "encodingFormat": "image/svg+xml"
    }
  ]
}
