Skip to main content

CLI Guide

n8nac is the terminal interface for n8n-as-code. It owns workspace environments, workflow sync, validation, AI context, and automation.

Local managed instances, Docker lifecycle, and tunnels belong to n8n-manager.

Install

npx --yes n8nac <command>

Optional global install:

npm install -g n8nac

For prerelease work, keep the CLI and manager on matching tags:

npx --yes n8nac@next <command>
npx --yes @n8n-as-code/n8n-manager@next <command>
Migrating from @n8n-as-code/cli

The old package name was @n8n-as-code/cli. Remove it if n8nac --help shows an outdated command list.

npm uninstall -g @n8n-as-code/cli

Command Groups

GroupCommandPurpose
Primary Usagen8nac envWorkspace environments
Workspace Inspectionn8nac workspaceV4 workspace snapshot
Managed Local Instancesn8n-managerLocal managed instances and tunnels

For a compact overview, see the Command Glossary.

Quick Start

Remote n8n environment

n8nac env add Dev --base-url https://n8n.example.com --workflows-path workflows/dev
n8nac env auth set Dev --api-key-stdin
n8nac env use Dev
n8nac update-ai

Local managed instance

n8n-manager instance list
n8nac env add Local --managed-instance <id> --workflows-path workflows/local
n8nac env use Local
n8nac update-ai

First workflow loop

n8nac list
n8nac pull <workflow-id>
n8nac push workflows/dev/my-workflow.workflow.ts --verify

env

Use env for normal workspace configuration.

n8nac env list
n8nac env status
n8nac env add Dev --base-url <url> --workflows-path workflows/dev
n8nac env add Local --managed-instance <id> --workflows-path workflows/local
n8nac env use Dev
n8nac env auth set Dev --api-key-stdin
n8nac env auth clear Dev
n8nac env remove Dev

Remote environments

Remote environments store the URL in n8nac-config.json, but the API key stays local.

n8nac env add Staging --base-url https://staging.example.com --workflows-path workflows/staging
n8nac env auth set Staging --api-key-stdin

Several accounts on one n8n URL

n8nac env auth set binds the key to the environment, not to the URL. Two environments that point at the same base URL — for example one account per n8n user on a single instance — each keep their own key.

n8nac env add Prod --base-url https://n8n.example.com --workflows-path workflows/prod
n8nac env add Preprod --base-url https://n8n.example.com --workflows-path workflows/preprod

printf '%s' "$PROD_N8N_API_KEY" | n8nac env auth set Prod --api-key-stdin
printf '%s' "$PREPROD_N8N_API_KEY" | n8nac env auth set Preprod --api-key-stdin

n8nac env status <name> --json reports which key a remote environment resolves, in apiKeySource:

apiKeySourceWhere the key comes from
envN8NAC_ENV_<ENVIRONMENT>_API_KEY or N8NAC_TARGET_<TARGET>_API_KEY
workspace-environmentstored for this environment by env auth set
workspace-localstored for the shared target by the VS Code environment editor, used by environments that have no key of their own
globalstored on the global n8n-manager instance matching the URL

env add --api-key and env update --api-key bind the key to that one environment and never to the shared target, so configuring one environment cannot change the key another environment authenticates with.

Use n8nac env auth clear <environment> to drop an environment key. The environment is then left without a credential of its own rather than borrowing one from another environment on the same URL, so commands fail with apiKeySource: missing until you store a new key.

Local managed instance environments do not take a stored key: they resolve either a scoped environment variable or the managed instance credentials, so env auth set and env add --api-key are rejected for them.

Local managed instance environments

These workspace environments reference a local n8n-manager instance.

n8n-manager instance list
n8nac env add Local --managed-instance <id> --workflows-path workflows/local

The workspace does not copy Docker paths, tunnel state, logs, or local secrets.

workspace

Use workspace for inspection. Use env status for effective runtime readiness.

n8nac workspace status --json
n8nac env status --json

Sync Commands

list

n8nac list
n8nac list --local
n8nac list --remote
n8nac list --search billing
n8nac list --sort name
n8nac list --raw

Status values:

StatusMeaning
TRACKEDLocal and remote workflow exist (git-style "tracked"). Alignment is checked explicitly by n8nac fetch <id>, and implicitly at pull/push time via direct hash comparison. n8nac list may also surface a drift flag in --json output when local or remote changes are detected since the last sync.
CONFLICTBoth local and remote changed since the last synced base (detected at pull/push)
EXIST_ONLY_LOCALLYLocal workflow has not been pushed
EXIST_ONLY_REMOTELYRemote workflow has not been pulled

Note: n8nac list is intentionally lightweight (no full payload diff, no TypeScript roundtrip per workflow). It reports which workflows are tracked and, when state is available, whether each side has drifted since the last sync. Use n8nac fetch <id> for authoritative per-workflow alignment before pulling.

Drift in --json output

n8nac list --json adds a drift object to each TRACKED workflow whose sync record in .n8n-state.json carries both a lastSyncedHash and a lastSyncedAt:

{
"id": "sx19mWQeBVouBgdO",
"name": "Carousel",
"filename": "Carousel.workflow.ts",
"status": "TRACKED",
"drift": { "local": false, "remote": true },
"lastSyncedAt": "2026-06-16T22:25:13.933Z",
"remoteUpdatedAt": "2026-06-16T22:45:28.755Z"
}
FieldMeaning
drift.localThe local file's hash differs from lastSyncedHash. Absent when the file could not be parsed during the scan.
drift.remoteThe remote updatedAt is newer than lastSyncedAt — the workflow was edited in the n8n UI since the last sync. Absent when the instance returned no updatedAt for that workflow, in which case remoteUpdatedAt is absent too.
drift (whole object)Absent for workflows that are not TRACKED, and for those whose sync record lacks lastSyncedHash or lastSyncedAt — there is no base to compare against.

An absent axis means unknown, never "unchanged": only false asserts that a side has not moved. Both axes can be absent at once (an unparseable file on an instance that reports no updatedAt), which reads as "there is a sync base, but nothing could be determined". Treating a missing axis as "no drift" would recreate the very false-alignment this signal exists to surface.

drift.remote is a timestamp comparison, not a content comparison: a workflow re-saved in the n8n UI with no effective change still reports remote: true. Run n8nac fetch <id> to confirm whether the payload actually differs.

find

n8nac find billing
n8nac find wf-123 --raw
n8nac find importer --limit 10

pull

n8nac pull <workflow-id>

Pull downloads one workflow and refuses to overwrite when a conflict is detected.

push

n8nac push workflows/dev/my-workflow.workflow.ts
n8nac push workflows/dev/my-workflow.workflow.ts --verify

Push uploads one local workflow and uses optimistic concurrency checks.

promote

n8nac promote workflows/dev/my-workflow.workflow.ts --from Dev --to Prod --dry-run
n8nac promote workflows/dev/my-workflow.workflow.ts --from Dev --to Prod
n8nac promote --from Dev --to Prod --dry-run
n8nac promote --from Dev --to Prod --no-push
n8nac promote --from Dev --to Prod --promotion-config config/promotion.json --json

Promote copies TypeScript workflows from one workspace environment to another, adapts them for the target environment, and pushes them to n8n unless --no-push is used.

The positional path is optional:

  • With a path, promote handles one *.workflow.ts file inside the source environment workflowsPath.
  • Without a path, promote recursively finds all non-hidden *.workflow.ts files in the source environment workflowsPath and preserves their relative paths in the target environment.

Promotion adapts workflows before writing or pushing:

  • target project metadata is rewritten to the target environment project
  • source workflow IDs and archived/home-project metadata are removed for new target workflows
  • known target workflow IDs are reused for updates
  • credential references are remapped by saved binding, explicit override, or unique target credential match by type and name
  • supported Execute Workflow references are remapped by saved binding, source workflow relation, explicit override, or unique target workflow name match

Promotion stores discovered source-to-target bindings in n8nac-promotion.json by default. Existing bindings are reused first, target inventory discovery is used for initial create/update planning, and missing or ambiguous references block promotion before push.

Options:

OptionEffect
--from <environment>Source environment name or ID
--to <environment>Target environment name or ID
--dry-runShow the planned promotion without writing workflow files, pushing, or saving bindings
--no-pushWrite adapted files to the target workflowsPath without pushing them to n8n
--overwriteReplace an existing local target file when no target workflow ID is known
--promotion-config <path>Read and write promotion bindings from a custom config path
--jsonPrint the promotion result as JSON

--dry-run still reads the target workflow inventory so the plan can report create vs update accurately. It does not write local workflow files, call push, or update n8nac-promotion.json.

The promotion config has a stable v1 shape:

{
"version": 1,
"routes": {
"Dev->Prod": {
"bindings": {
"workflows": {
"source-workflow-id": "target-workflow-id"
},
"credentials": {
"source-credential-id": "target-credential-id"
}
},
"workflowOverrides": {},
"credentialOverrides": {},
"nameRules": []
}
}
}

Use overrides when a target name is ambiguous or intentionally different:

{
"version": 1,
"routes": {
"Dev->Prod": {
"workflowOverrides": {
"source-workflow-id": { "targetId": "prod-workflow-id" },
"Source Workflow Name": { "targetName": "Production Workflow Name" }
},
"credentialOverrides": {
"httpBasicAuth::source-credential-id": { "targetId": "prod-credential-id" },
"httpBasicAuth::Shared Credential": { "targetName": "Production Credential" }
},
"nameRules": [
{ "kind": "workflow", "from": " Dev$", "to": " Prod" },
{ "kind": "credential", "from": " Dev$", "to": " Prod" }
]
}
}
}

Promotion does not create credentials in the target environment. Create or map target credentials first, or use n8nac credentials ... helpers. After a pushed single-workflow promotion, the CLI prints a workflow credential-required command so you can check target credential readiness.

resolve

n8nac resolve <workflow-id> --mode keep-current
n8nac resolve <workflow-id> --mode keep-incoming
  • keep-current: keep the local version and force-push it.
  • keep-incoming: keep the remote version and force-pull it.

Runtime Facade Commands

These commands operate through the active environment and n8n API.

n8nac verify <workflow-id>
n8nac workflow present <workflow-id> --json
n8nac workflow credential-required <workflow-id> --json
n8nac workflow activate <workflow-id>
n8nac test-plan <workflow-id> --json
n8nac test <workflow-id> --data '{"foo":"bar"}'
n8nac execution list --workflow-id <workflow-id> --limit 5 --json
n8nac execution get <execution-id> --include-data --json

Credential readiness:

n8nac credentials recipes
n8nac credentials starter-kits
n8nac credentials inventory --json
n8nac credentials ensure http-bearer --value token=... --json
n8nac credentials test http-bearer --json
n8nac credentials delete http-bearer --json

Low-level credential API helpers:

n8nac credential schema openAiApi
n8nac credential create --type openAiApi --name "My OpenAI" --file cred.json --json
n8nac credential list --json

Prefer --file over inline secret values so secrets do not end up in shell history.

AI Context And Skills

n8nac update-ai
n8nac skills search "google sheets"
n8nac skills node-info googleSheets
n8nac skills validate workflows/dev/my-workflow.workflow.ts

update-ai refreshes local context files for agents, including AGENTS.md, VS Code agent files, portable skills, snippets, and schemas.

Conversion

n8nac convert workflow.json --format typescript
n8nac convert workflow.workflow.ts --format json
n8nac convert-batch workflows/ --format typescript

Workspace Config

Current config is environment-based and safe to commit when it contains no secrets:

{
"version": 4,
"activeEnvironmentId": "dev",
"environments": [
{
"id": "dev",
"name": "Dev",
"environmentTargetId": "dev",
"projectId": "personal",
"projectName": "Personal",
"workflowsPath": "workflows/dev"
}
],
"environmentTargets": [
{
"id": "dev",
"name": "Dev",
"kind": "external-instance",
"url": "https://n8n.example.com"
}
]
}

API keys are stored locally with n8nac env auth set <env> --api-key-stdin.

workflowsPath is generated from the environment name when the environment is created. It is stable after creation, so changing the environment display name, target instance, or target project does not move the workflow directory. When workflowsPath is changed explicitly, n8nac moves existing workflow files to the new directory when the destination is empty.

In config examples, kind: "external-instance" is the persisted target kind for a remote n8n URL. Prefer the user-facing term "remote n8n environment" outside raw config discussions.

Scripting Example

#!/bin/bash
set -euo pipefail

printf '%s' "$STAGING_N8N_API_KEY" | n8nac env auth set Staging --api-key-stdin
n8nac env use Staging
n8nac list --raw
n8nac push workflows/staging/my-workflow.workflow.ts --verify

For multiple remote environments, create one environment per target and switch with n8nac env use <name>.

Troubleshooting

Check the active context:

n8nac env list
n8nac env status --json
n8nac workspace status --json

Refresh a remote API key:

n8nac env auth set <environment> --api-key-stdin

See Troubleshooting for more fixes.