ap project replace CLI mirrors a project’s flows, table schemas, and folders from one Activepieces deployment to another using direct API calls. Use it from CI/CD to promote work between independent staging and production instances when Git-based Project Releases aren’t a fit.
Prerequisites
- The Environments feature must be enabled on the destination platform’s plan — see Project Releases prerequisites.
- You need a platform-scoped API key (
SERVICEprincipal) on both instances. The same project id format must be used on each side. - Both deployments must share the same major Activepieces version, and the destination version must be greater than or equal to the source version.
What gets mirrored
| Resource | Behavior |
|---|---|
| Flows | Created / updated / deleted on the destination by externalId. |
| Table schemas | Schema only — fields, name, externalId. Row data is never copied. |
| Folders | Mirrored by externalId. |
| Required pieces | Auto-installed on the destination at the source’s exact pinned version (official + custom from npm). Replace aborts before any other writes if any install fails. |
| Connections | Metadata auto-mirrored (externalId, pieceName, displayName); secret values never cross the wire. New connections land on the destination as placeholders with status: MISSING. Operator authorizes each one in the destination UI before flows can run. |
| MCP servers, agents, project metadata, custom domains, app credentials | Out of scope. Not touched. |
Installation
activepieces CLI is shipped with each Activepieces release; pin the version that matches your destination instance to keep request shapes aligned.
Command
Flags
| Flag | Required | Purpose |
|---|---|---|
--source-url | yes | Base URL of the source instance (no trailing slash needed). |
--source-api-key | flag or env | Platform API key for the source. Falls back to AP_SOURCE_API_KEY. |
--source-project | yes | Project id on the source instance. |
--dest-url | yes | Base URL of the destination instance. |
--dest-api-key | flag or env | Platform API key for the destination. Falls back to AP_DEST_API_KEY. |
--dest-project | yes | Project id on the destination instance. |
--json | no | Emit machine-readable JSON instead of a human summary. |
Exit codes
| Code | Meaning |
|---|---|
0 | Apply succeeded; every item applied cleanly. |
1 | Apply succeeded but at least one item failed. Inspect failed[] in the response. |
2 | Server-side preflight failed (422). No writes occurred. |
3 | Server abort: piece install failed (502), generic 5xx, or another replace was already in progress on the destination project (409). |
4 | Local CLI / transport error — bad URL, unreachable host, invalid API key. |
What happens when you run it
- CLI lists the source project’s flows, folders, and table schemas via the source instance’s REST API.
- CLI packages them into a
ProjectReplaceRequestandPOSTs to/v1/projects/:projectId/replaceon the destination. - Destination acquires a per-project
NoWaitlock. If another replace is in flight, it returns409 REPLACE_IN_PROGRESS. - Destination preflight (no writes if any of these fail):
- Activepieces version is parsed; same major +
dest >= source. - For every source connection: if the destination already has one with the same
externalId, itspieceNamemust match.
- Activepieces version is parsed; same major +
- Install phase — for each entry in
requiredPiecesnot already on dest at the exact pinned version, the server installs it from npm (packageType: REGISTRY, scope: platform). All installs are attempted; if any fail, the response aborts with502listing every failure. No other writes have happened at this point — folders, tables, flows, and connections are still untouched. - Apply phase — connections, then folders, then tables, then flows are created/updated. Deletes run in reverse order. Each item runs in its own try/catch; per-item failures are collected, systemic 5xx errors abort the run.
- Audit event
project.replacedis emitted only when the apply phase ran — onSUCCESSorPARTIAL_FAILURE. Rejected attempts (preflight failure, install failure, lock contention) are not audited.
Output
Human-readable (default)
--json
Preflight failures (exit 2)
AP_VERSION_MISMATCH, PIECE_VERSION_MISMATCH, CONNECTION_PIECE_MISMATCH.
Install failures (exit 3, HTTP 502)
Idempotency and retries
Re-running the CLI after a partial failure converges to the source state. Items applied on the previous run are detected as unchanged via a typed deep-equality check and skipped; failed items are retried. The destination is left in a partially-applied state on hard failure — by design, since pause/restore would force downtime on every successful release. The apply phase is not wrapped in a single database transaction, and that’s deliberate:- Partial-success semantics (
207+failed[]) require successfully-applied items to persist across a sibling failure. A single transaction would roll back every successful folder/table/connection just because one flow’s republish failed. - Flow republish dispatches BullMQ jobs and trigger-source registrations that can’t sit inside a SQL transaction.
- Recovery is by re-run, not rollback. Every operation matches by
externalId, so a process crash mid-apply leaves the destination in some intermediate state that the next run’s diff phase detects and finishes. CI/CD’s natural retry handles process-level failures (OOM, SIGKILL, deploy timeout).
0.
Connections
Connection metadata (externalId, pieceName, displayName) is mirrored to the destination as placeholder records withstatus: MISSING. Secret values (OAuth tokens, API keys, etc.) never cross the wire — each instance keeps its own.
After a replace, the CLI prints any connections that still need authorization on the destination:
ACTIVE.
If a connection on the destination already exists with the same externalId but a different pieceName, the replace fails preflight with CONNECTION_PIECE_MISMATCH so the conflict can be resolved without overwriting unrelated flows.
GitHub Actions example
Troubleshooting
409 REPLACE_IN_PROGRESS
409 REPLACE_IN_PROGRESS
Another replace is running against the same destination project. Wait a few seconds and retry — the lock is per-project and released as soon as the previous run finishes.
Install failed (502)
Install failed (502)
The CLI passed preflight but a required piece couldn’t be installed on the destination. Check
failures[*].message in the response — most commonly the piece doesn’t exist on npm at that version, the registry is unreachable, or the engine crashed during metadata extraction. The replace aborted before any writes; fix the install issue and re-run.PIECE_VERSION_MISMATCH
PIECE_VERSION_MISMATCH
The destination already has the piece installed at a different version than the source. Either upgrade/downgrade the destination piece manually, or update the source flow to use a version compatible with what’s on the destination.
CONNECTION_PIECE_MISMATCH
CONNECTION_PIECE_MISMATCH
The destination already has a connection with the same
externalId but for a different piece. Either rename the source’s externalId or delete/replace the conflicting connection on the destination before re-running.AP_VERSION_MISMATCH
AP_VERSION_MISMATCH
The destination is on an older version or a different major than the source. Upgrade the destination first.
Feature is disabled
Feature is disabled
The destination platform’s plan does not have the Environments feature enabled. Contact your platform owner.