Skill v1.0.2
currentAutomated scan96/100~4 modified
name: n8n-cli description: Use the n8n CLI to manage workflows, credentials, executions, and more on an n8n instance. Use when the user asks to interact with n8n, automate workflows, manage credentials, or operate their instance from the command line. allowed-tools: Bash(n8n-cli:), Bash(echo:), Bash(cat:*), Read, Write metadata: version: "1.0.2"
n8n CLI
The n8n-cli command-line tool manages an n8n instance via its REST API. It auto-detects piped output and switches to JSON, making it composable for scripts and LLM tool use.
Setup
# Interactive login (saves to ~/.n8n-cli/config.json)n8n-cli login# Or configure directlyn8n-cli config set-url https://my-instance.n8n.cloudn8n-cli config set-api-key n8n_api_...# Or use environment variables (no config file needed)export N8N_URL=https://my-instance.n8n.cloudexport N8N_API_KEY=n8n_api_...
Global Flags
Every command supports these flags:
| Flag | Short | Description | |
|---|---|---|---|
--url | -u | Instance URL (overrides config/env) | |
--apiKey | -k | API key (overrides config/env) | |
--format | -f | Output format: table, json, id-only | |
--json | Shorthand for --format=json | ||
--jq | jq-style filter (implies --json), e.g. '.[0].id', '.[].name' | ||
--quiet | -q | Suppress output | |
--no-header | Hide table headers (for awk/cut parsing) | ||
--debug | Print HTTP details to stderr |
Auto-JSON: When stdout is piped (not a TTY), output defaults to JSON automatically.
Workflows
# List all workflowsn8n-cli workflow list# Filter workflowsn8n-cli workflow list --activen8n-cli workflow list --tag=productionn8n-cli workflow list --name="My Workflow"n8n-cli workflow list --limit=5# Get a single workflow (full JSON with nodes and connections)n8n-cli workflow get <id># Extract just node names from a workflown8n-cli workflow get <id> --jq '.nodes[].name'# Create a workflow from JSONn8n-cli workflow create --file=workflow.jsoncat workflow.json | n8n-cli workflow create --stdin# Update a workflown8n-cli workflow update <id> --file=updated.jsoncat updated.json | n8n-cli workflow update <id> --stdin# Activate / deactivaten8n-cli workflow activate <id>n8n-cli workflow deactivate <id># Delete a workflown8n-cli workflow delete <id># Transfer to another projectn8n-cli workflow transfer <id> --project=<projectId># List tags on a workflown8n-cli workflow tags <id>
Executions
# List recent executionsn8n-cli execution listn8n-cli execution list --workflow=<id> --status=error --limit=10# status options: canceled, error, running, success, waiting# Get execution detailsn8n-cli execution get <id>n8n-cli execution get <id> --include-data # includes full node I/O# Retry a failed executionn8n-cli execution retry <id># Stop a running executionn8n-cli execution stop <id># Delete an executionn8n-cli execution delete <id>
Credentials
# List credentialsn8n-cli credential list# Get credential metadata (not secrets)n8n-cli credential get <id># Get the schema for a credential type (shows required fields)n8n-cli credential schema notionApin8n-cli credential schema slackOAuth2Api# Create a credentialn8n-cli credential create --type=notionApi --name='My Notion' --data='{"apiKey":"..."}'n8n-cli credential create --type=notionApi --name='My Notion' --file=cred.jsoncat cred.json | n8n-cli credential create --type=notionApi --name='My Notion' --stdin# Delete / transfern8n-cli credential delete <id>n8n-cli credential transfer <id> --project=<projectId>
Tip: Use credential schema <type> to discover required fields before creating.
Projects
n8n-cli project listn8n-cli project get <id>n8n-cli project create --name="My Project"n8n-cli project update <id> --name="New Name"n8n-cli project delete <id># Team managementn8n-cli project members <id>n8n-cli project add-member <id> --user=<userId> --role=<role>n8n-cli project remove-member <id> --user=<userId>
Tags
n8n-cli tag listn8n-cli tag create --name=productionn8n-cli tag update <id> --name=stagingn8n-cli tag delete <id>
Variables
n8n-cli variable listn8n-cli variable create --key=API_ENDPOINT --value=https://api.example.comn8n-cli variable update <id> --key=API_ENDPOINT --value=https://new-api.example.comn8n-cli variable delete <id>
Data Tables
# CRUDn8n-cli data-table listn8n-cli data-table get <id>n8n-cli data-table create --name=Inventory --columns='[{"name":"item","type":"string"},{"name":"qty","type":"number"}]'n8n-cli data-table delete <id># Row operationsn8n-cli data-table rows <id>n8n-cli data-table add-rows <id> --file=rows.jsonn8n-cli data-table update-rows <id> --file=rows.jsonn8n-cli data-table upsert-rows <id> --file=rows.jsonn8n-cli data-table delete-rows <id> --ids=row1,row2,row3# All row commands support --stdincat rows.json | n8n-cli data-table add-rows <id> --stdin
Users
n8n-cli user listn8n-cli user get <id>
Promotions
Move projects between instances through a Git repository. A provider holds the credentials. An environment sets up one direction on a repository: promote pushes to Git, apply imports from Git. The instance can have one environment for each direction.
# 1. Create a provider. An SSH provider returns a public key to add as a deploy key.# The response carries the provider fields at the top level, so `.id` and# `.publicKey` both work with --jq and --format=id-only.echo '{"name":"GitHub","type":"git","auth":{"authType":"ssh-key","keyType":"ed25519"}}' \| n8n-cli promotion-provider create --stdin --json > provider.jsonjq -r '.id' provider.json # use as providerId in step 2jq -r '.publicKey' provider.json # add to the repository as a deploy key# 2. Create an environment on that provider for each direction you need.n8n-cli promotion-environment create --file=promote.jsonn8n-cli promotion-environment create --file=apply.json# 3. Connect each environment before you use it.n8n-cli promotion-environment connect <promoteId>n8n-cli promotion-environment connect <applyId># 4. Promote from this instance, or apply to it.n8n-cli promotion-environment promote <promoteId>n8n-cli promotion-environment apply <applyId># A project that exists only in the Apply package must exist here before you# apply to it from its project. Create it empty, with the same ID:n8n-cli promotion-environment list-missing-projects <applyId>n8n-cli promotion-environment create-missing-projects <applyId> # exits 1 if one fails
GitLab setup. A GitLab provider lists its repositories and branches, so read the remote URL and the branch names from it instead of guessing them:
# gitlab-provider.json:# {"name":"GitLab","type":"gitlab","config":{"schemaVersion":1,"baseUrl":"https://gitlab.example.com"},# "auth":{"authType":"token","password":"<GitLab access token>"}}n8n-cli promotion-provider create --file=gitlab-provider.json --format=id-only # providerIdn8n-cli promotion-provider list-repositories <providerId> --search=flows --json # remoteUrl, fullPathn8n-cli promotion-provider list-branches <providerId> <fullPath> --json # isDefault marks the default branch# Put remoteUrl and a branch name in the environment JSON, then create and connect as above.
Apply a reviewed commit only:
# Review the changes. Take the commit SHA from the same response as the rows:# a second request can read a newer commit.n8n-cli promotion-environment list-changes <projectId> apply --json > reviewed-changes.jsonjq '{commitSha, source, resources}' reviewed-changes.json# The branch comes from the Apply environment.n8n-cli promotion-environment get <applyId> --jq '.settings.branchName'n8n-cli promotion-environment apply <applyId> \--expected-environment-id=<applyId> --expected-branch=<branch> --expected-commit-sha=<full sha># Exit 3: the source changed since the review. Review again.# Exit 4: the Apply check found missing bindings, access requirements, or conflicts. Resolve them,# then run the apply-continue command that apply printed:n8n-cli promotion-environment apply-continue <applyId> \--expected-environment-id=<applyId> --expected-branch=<branch> --expected-commit-sha=<full sha># To also apply destructive data table changes and delete the team projects that the# package does not hold, add --confirm-destructive-changes.
Promote or apply a selection of a project:
# The table lists each changed resource with its TYPE and ID. Select workflows with -w and# folders with --folder; the project is always included. A changed credential, data table,# variable, or tag can't be selected: select a workflow from its USEDBY column instead.n8n-cli promotion-environment list-changes <projectId> promoten8n-cli promotion-environment promote-selection <projectId> -w <workflowId> --folder <folderId># Apply only the reviewed selection. Take the source and the revision from the same preview.n8n-cli promotion-environment list-changes <projectId> apply --json > preview.jsonjq '{commitSha, source, revision}' preview.jsonn8n-cli promotion-environment apply-selection <projectId> -w <workflowId> \--expected-environment-id=<source.environmentId> --expected-branch=<source.branchName> \--expected-commit-sha=<commitSha> --expected-preview-revision=<revision># Exit 3: the source or the preview changed since the review. Review again.
Environment JSON for step 2 (promote.json; apply.json uses "direction":"apply" and "settings":{"schemaVersion":1,"branchName":"main"}):
{"name": "Production","scope": "instance","providerId": "prov-1","remote": { "schemaVersion": 1, "remoteUrl": "git@github.com:acme/flows.git" },"direction": "promote","settings": { "schemaVersion": 1, "baseBranchName": "main", "createBranchOnPromotion": false }}
# Providersn8n-cli promotion-provider listn8n-cli promotion-provider get <id> # re-read the public key; the list omits itecho '{"name":"New name"}' | n8n-cli promotion-provider update <id> --stdinn8n-cli promotion-provider delete <id> # fails while an environment uses itn8n-cli promotion-provider list-repositories <id> --search=<text> --limit=20 # GitLab onlyn8n-cli promotion-provider list-branches <id> <fullPath or repository id> # GitLab only# Environmentsn8n-cli promotion-environment list --scope=instance --direction=promoten8n-cli promotion-environment list --provider=<providerId>n8n-cli promotion-environment get <id>echo '{"name":"New name"}' | n8n-cli promotion-environment update <id> --stdinn8n-cli promotion-environment delete <id>n8n-cli promotion-environment disconnect <id># Change the settings. The write replaces the whole settings object,# so send every setting you want to keep. Direction and scope cannot change.echo '{"settings":{"schemaVersion":1,"baseBranchName":"main","createBranchOnPromotion":false}}' \| n8n-cli promotion-environment update <promoteId> --stdin# Link projects to a "projects"-scoped environmentn8n-cli promotion-environment list-projects <id>n8n-cli promotion-environment add-project <id> <projectId>n8n-cli promotion-environment remove-project <id> <projectId>
Key points:
- Pass every JSON body through
--stdinor--file, never through a flag. This
keeps credentials off the command line.
typeisgitorgitlab. Forgit,authTypeisssh-keyortoken, and
token means an HTTP(S) username and password. For gitlab, authType is token only: password is the GitLab access token (api and write_repository), config.baseUrl is required, and n8n ignores username. publicKey is null for a token provider. The token user needs the Developer role or higher to open merge requests. Protected branches can require the Maintainer role to push, and to merge when a reviewer approves.
list-repositoriesandlist-brancheswork forgitlabproviders only. Agit
provider fails with an error. Pass the repository as plain fullPath (acme/flows), id, or an environment's remote.repositoryPath. Do not URL-encode it. Read remoteUrl, fullPath and branch names from --json output: the table shortens values longer than 60 characters to ....
createBranchOnPromotionis always required in promote settings.promoteandapplywork on aninstanceenvironment of their direction
only, and need it connected first. Connecting one environment does not make another ready.
- A project can have one environment for each direction. A direction without a
project environment uses the instance environment.
applyandapply-continueimport nothing on exit code3(source-changed)
or 4 (blocked). Check $?, or the status field in JSON output, before you read counts. With --json, a blocked result lists what is missing under check.
- The
--expected-environment-id,--expected-branch, and
--expected-commit-sha flags go together: pass all three or none. The commit SHA must be the full SHA. --expected-preview-revision is separate and works only with apply-selection and apply-selection-continue.
- API key scopes for this group are named
promotions:*.promotealso needs
variable:list when the workflows reference variables.
Other
# Security auditn8n-cli auditn8n-cli audit --categories=credentials,nodes# Source controln8n-cli source-control pull# View confign8n-cli config show
Composability Patterns
The CLI is designed to be piped and composed:
# Get all workflow IDsn8n-cli workflow list --jq '.[].id'# Get the name of the first workflown8n-cli workflow list --jq '.[0].name'# Export a workflow to a filen8n-cli workflow get 1234 --json > workflow-backup.json# Find failing executions for a workflown8n-cli execution list --workflow=1234 --status=error --json# Pipe workflow JSON for modificationn8n-cli workflow get 1234 --json | jq '.name = "Updated Name"' | n8n-cli workflow update 1234 --stdin# Table output without headers for shell parsingn8n-cli workflow list --no-header | awk '{print $1}'# Debug API callsn8n-cli workflow list --debug 2>debug.log
Workflow JSON Structure
When creating or updating workflows, the JSON follows this structure:
{"name": "My Workflow","nodes": [{"name": "Start","type": "n8n-nodes-base.manualTrigger","position": [250, 300],"parameters": {}},{"name": "HTTP Request","type": "n8n-nodes-base.httpRequest","position": [450, 300],"parameters": {"url": "https://api.example.com/data","method": "GET"}}],"connections": {"Start": {"main": [[{ "node": "HTTP Request", "type": "main", "index": 0 }]]}}}
Key points:
nodes[].typefollows the patternn8n-nodes-base.<nodeName>for built-in nodesconnectionsis keyed by source node name, withmainoutput arrays- Each connection specifies target
node,type(usuallymain), and outputindex - Use
workflow get <id> --jsonto see real examples from the instance