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>
@n8n-as-code/cliThe 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
| Group | Command | Purpose |
|---|---|---|
| Primary Usage | n8nac env | Workspace environments |
| Workspace Inspection | n8nac workspace | V4 workspace snapshot |
| Managed Local Instances | n8n-manager | Local 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:
apiKeySource | Where the key comes from |
|---|---|
env | N8NAC_ENV_<ENVIRONMENT>_API_KEY or N8NAC_TARGET_<TARGET>_API_KEY |
workspace-environment | stored for this environment by env auth set |
workspace-local | stored for the shared target by the VS Code environment editor, used by environments that have no key of their own |
global | stored 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:
| Status | Meaning |
|---|---|
TRACKED | Local 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. |
CONFLICT | Both local and remote changed since the last synced base (detected at pull/push) |
EXIST_ONLY_LOCALLY | Local workflow has not been pushed |
EXIST_ONLY_REMOTELY | Remote workflow has not been pulled |
Note:
n8nac listis 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. Usen8nac 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"
}
| Field | Meaning |
|---|---|
drift.local | The local file's hash differs from lastSyncedHash. Absent when the file could not be parsed during the scan. |
drift.remote | The 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,
promotehandles one*.workflow.tsfile inside the source environmentworkflowsPath. - Without a path,
promoterecursively finds all non-hidden*.workflow.tsfiles in the source environmentworkflowsPathand 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:
| Option | Effect |
|---|---|
--from <environment> | Source environment name or ID |
--to <environment> | Target environment name or ID |
--dry-run | Show the planned promotion without writing workflow files, pushing, or saving bindings |
--no-push | Write adapted files to the target workflowsPath without pushing them to n8n |
--overwrite | Replace 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 |
--json | Print 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.