Use environments programmatically with the public API
You can manage n8n environments from scripts and pipelines using the public API, without opening the n8n UI. This lets you automate promotion between instances, for example moving work from a development instance to production on a schedule or as part of a CI/CD pipeline.
This page explains how the source control API endpoints work together. It assumes you've already connected your instances to Git and understand how push and pull behave in the UI. For that background, read Push and pull and Copy work between environments.
The endpoints
The public API exposes three source control operations:
/source-control/status
GET
Previews the pending changes between your instance and Git, in either direction.
/source-control/push
POST
Commits and pushes local changes to the connected Git branch.
/source-control/pull
POST
Fetches changes from the connected Git branch into the instance.
push and pull are the same actions as the Push and Pull buttons in the source control menu. status matches the file list the UI shows in the push and pull modals before you confirm. The API exposes it as its own endpoint so a script can do the same review the UI does inline.
For the full request and response schemas, refer to the Endpoint reference.
Authentication and scopes
Every request needs an API key sent in the X-N8N-API-KEY header. On Enterprise plans, you can scope each key to the minimum it needs:
sourceControl:pull
Pull changes into the instance.
sourceControl:push
Push local changes to Git.
sourceControl:read
Read the status of pending changes.
Scoping keys this way is how you enforce direction. A production instance that should only ever receive changes gets a key with sourceControl:pull, so it can't push. Add sourceControl:read too if you also want that key to preview incoming changes with status before pulling them.
Check the status, then act
Don't push blind. push requires a fileNames array and only commits the files you list there, so call status first to see what's eligible.
The steps should be:
Call
statusto see what's eligible.Decide what to include: every file to push everything, or a subset to push only some of it.
Build
fileNamesfrom the files you picked, keeping only each file'sidandtype.Call
pushwith that array.
Call status with a direction query parameter to choose which side you're previewing:
Each file in the response includes its id, name, type (such as workflow or credential), and status (such as modified or new):
Promotion flow from development to read-only production
This is a common setup: you make changes on a development instance, then promote them to a production instance that only ever receives updates. This example uses a single Git branch shared by both instances.
Give each instance a scoped API key:
The development instance API key has
sourceControl:readandsourceControl:push.The production instance API key has
sourceControl:readandsourceControl:pull.
1. Preview the changes on development. Call status with direction=push to see what n8n would commit:
2. Push from development to Git. Send a commit message and the files to promote, listed in fileNames as id/type pairs. Include every file from step 1's status response to promote in entirety, or a subset to promote part of it:
3. Preview the changes on production. Call status with direction=pull to see what n8n would apply, before it touches anything:
4. Pull into production. The production instance reads the same branch and applies the changes. Use force to accept the incoming version, and set autoPublish to publish workflows as they arrive:
The autoPublish parameter accepts none (the default), published (only republish workflows that are already published on production), or all (publish every pulled workflow). For more details on each mode, refer to Push and pull.
Scheduled backup flow
To version your instance in Git on a schedule, fetch every eligible file and push everything on a timer. Run this from a cron job, a CI pipeline, or an n8n workflow on a Schedule Trigger
1. Get every eligible file. Call status with direction=push:
2. Push the full list. Map every file the status response returned to its id and type, and pass that as fileNames. Unlike the promotion flow, this job runs unattended and sets force to true. This skips the conflict check and always overwrites the branch with the instance's current state. Make sure the instance holds the versions you want before each scheduled run, or you risk overwriting more recent changes in Git.
Handle push conflicts
A file is in conflict when both the instance and Git have changed it since the last sync. status marks these files with "conflict": true. If you call push without force and any selected file is in conflict, n8n rejects the whole push with a 409 response:
To push anyway and overwrite the conflicting files in Git with the instance's version, set force to true:
What gets pushed and pulled
The API commits and applies the same resources as the UI: workflows, credential stubs, variable stubs, data table schemas, projects, folders, and tags. For the full list and the merge behavior for each resource type, refer to Push and pull.
Related resources
Source control: Request and response schemas for the source control endpoints.
Endpoint reference: The complete public API schemas.
Last updated
Was this helpful?