Plane docs
Guides

Promotion between environments

Copy a definition, resolve every name it references in the target, or write nothing.

An agent definition references connections, providers and secrets by name. Which is exactly what makes it promotable: a connection called orders in dev is called orders in prod, pointing at a different URL with a different credential.

PLANE_API_KEY=$PLANE_ORG_KEY npx plane promote support-bot --to prod
npx plane promote support-bot --to prod --map connection:orders=orders-prod --dry-run
// The same code path as the write, writing nothing.
await client.agents.promote({ agentId, to: "prod", dryRun: true })

await client.agents.promote({
  agentId,
  to: "prod",
  mapping: { connections: { orders: "orders-prod" } }
})

The CLI always runs a dry run first and prints the resolution, so a failure is legible in CI output without anybody reading JSON:

support-bot v7 → prod: cannot promote
  connection   orders  MISSING in prod
  provider     anthropic → anthropic-prod  MISSING in prod

Failure is total and enumerated. PromotionUnresolvedError lists every missing name at once — connections, providers and secrets — and nothing is written. The operation fails in CI instead of on the first production turn.

Promoting an identical definition twice creates one version, so it is safe on every green build.

An API key cannot promote

A key is an environment — that is what a key is — and promotion crosses two. It needs a cross-environment principal: a signed-in user with control in both, or an organization-scoped credential. This is the one operation whose authorization is two questions rather than one.

--map kind:from=to is repeatable and kind is connection, provider or secret. The common case needs no mapping at all; it exists for the organization that suffixed everything.