Use the portal, REST API or hosted MCP. Local MCP tools require MCP 0.36.0 or later.
A project groups environments such as staging and production. Each environment contains named components linked to existing catalog or custom applications. Linking does not deploy, change networking, copy variables or share credentials. Existing applications retain their IDs, history and runtime configuration.
Before you start
Use an account credential with read scope for inventory and deploy scope for changes. Named hosted MCP tools require unrestricted account credentials. Portal viewers can read the inventory; managing services requires the corresponding team permission and session CSRF token.
Applications must currently belong to your account, have a non-revoked agent and valid current server ownership. Attach only running or failed applications. Use lowercase names beginning with a letter, at most 48 characters, containing letters, numbers and internal hyphens.
Portal workflow
In My Apps → Projects and environments (below the App catalog, in both the Simple and Advanced views), create a project, create its environments and open the project to inspect its services. Select an environment and existing app, give the component a name and choose a role: web, worker, database or cache. Roles are organizational labels; selecting database does not validate an engine or provision a database.
Select a project name to view its environments, linked apps, recorded deployment results, active previews, primary domains, backup policy and team permissions. Variable groups show names only. Team permissions apply to the account. Certificate status is shown only when reported by the metadata; the current portal does not report certificate telemetry. Use Promote configuration, Deploy environment in order, Create preview or App settings to reach the existing controls. Opening the page does not deploy an app or create a backup.
Use the same component name and role in staging and production for the corresponding app. One app belongs to at most one environment, and a component name is unique within its environment. A conflicting association is refused. Detach requires confirmation and removes only the association; the application and its data remain.
The same panel edits variable groups, reviews and applies a configuration promotion and starts an ordered environment deploy. Click Open environments on a project first, so the environment lists are filled.
MCP workflow
impreza_create_projectwithname: "store"returnsproject_id.impreza_create_environmentwith thatproject_idandname: "staging"returnsenvironment_id. Repeat for production.impreza_attach_environment_servicewithenvironment_id,deployment_id,component: "web"androle: "web"links an existing application.impreza_list_projectswithout arguments returns project names and IDs. Passproject_idto load environments and authorized service metadata. Variable values are never returned.- To move an app, first call
impreza_detach_environment_servicewithenvironment_id,deployment_idandconfirm: true, then attach it to the intended environment.
These projects organize running applications. Saved deployment plans describe reusable deploy inputs and remain a separate feature.
REST requests
Use the standard authenticated API headers. IDs in these examples are placeholders; use the returned IDs.
POST /v1/platform/projects
Content-Type: application/json
{"name":"store"}
POST /v1/platform/projects/prj_RETURNED_ID/environments
Content-Type: application/json
{"name":"staging"}
POST /v1/platform/environments/env_RETURNED_ID/services
Content-Type: application/json
{"deployment_id":"dpl_EXISTING_APP","component":"web","role":"web"}
GET /v1/platform/projects lists projects. GET /v1/platform/projects/{id} loads the selected inventory. POST /v1/platform/environments/{id}/services/detach accepts only deployment_id and boolean confirm: true.
Verify and retry
Read the selected project after a write, especially if the response was lost. Repeating an identical project/environment name or association returns the existing identity. An app cannot be silently moved or relabeled by retrying an attach.
The inventory rechecks current ownership. Transferred, revoked or uninstalled applications are not exposed as active service entries. The owner of the organizational record can detach stale metadata by known ID without touching the workload. Pending application operations block membership changes.
Grouped image promotions require the same project, component and role across two different environments. A changed association invalidates the prepared promotion. Ungrouped source and destination apps can still use the standalone promotion flow.
Limits and errors
- Up to 50 projects per account, 20 environments per project and 100 linked services per environment. Load one project at a time for service details.
- Invalid fields or names:
400. Missing permissions:403. Unowned or unavailable resources:404. Conflicting association, busy app or stale promotion:409. Quota:429. Storage failure:503; read before retrying. - No automatic app provisioning. Inherited variable groups, dependency wiring, deployment ordering and rename/delete arrived in a later increment — see environment automation.
- Linking a database/cache does not yet create a connection binding or grant network access. Image promotion can interrupt traffic and does not migrate or roll back database contents.
PostgreSQL connections use these associations and require agent 0.6.13 or later.
Environment comparison and traffic switch
Available through the portal, REST API and hosted MCP. Local MCP requires 0.38.0+. Traffic switching requires agent 0.6.16+; update existing agents explicitly before using it.
Comparison pairs services by component within one project. GET /v1/platform/environments/{id}/compare?other=env_OTHER_ID requires read scope and unrestricted account credentials. The portal's comparison view and hosted MCP impreza_compare_environments return variable names grouped as equal, different or present on one side, source descriptors and connection state. Values and credential digests are never returned. A revoked server or lost current ownership hides its services.
A traffic switch moves one hostname between two running custom apps on the same agent. The destination must have no hostname; previews cannot participate. Use the portal review, or impreza_prepare_traffic_switch with deployment_id and target_deployment_id, then impreza_get_traffic_switch and impreza_apply_traffic_switch. Applying requires deploy scope, unrestricted account credentials, switch_id, the exact review_digest and confirm: true. REST equivalents are POST /v1/platform/deployments/custom/{id}/prepare-traffic-switch, GET /v1/platform/traffic-switches/{id} and POST /v1/platform/traffic-switches/{id}/apply.
The agent must announce traffic-switch-v1 and controlled-deployment support. The target must be running and any configured Docker healthcheck must be healthy. A local HTTPS route probe rejects connection errors and 5xx responses; it does not certify application correctness or public DNS/TLS. The source stays running. Brief interruption remains possible.
Both apps are reserved against conflicting changes. Ownership and the reviewed configuration are checked again; stale reviews return 409, unowned resources 404. A verified receipt updates hostname and saved routing variables together. On an ordinary failure the agent attempts rollback. If recovery cannot be verified, or the agent crashes during the move, do not retry blindly: routing must be reconciled before further app changes. An interrupted local proxy transaction retains private recovery evidence and blocks unrelated proxy changes as well; do not remove that evidence to unblock the queue. There is no canary split, cross-host traffic switch or automatic recovery guarantee after a host crash.
Variable groups, wiring and ordered deploys
Status: available through the REST API and hosted MCP since 25 September 2026, and local impreza-mcp 0.46.0 or later. The portal has covered variable groups, configuration promotion and the ordered environment deploy since 28 September 2026, and since 29 September 2026 it saves a group only against the revision it loaded, keeps a stored value you leave blank, and lists the environment's recent deploys. Environment wiring, rename and delete remain REST and MCP operations.
Variable groups
A variable group is one object of variables on a project or on one environment, replacing the twenty copied lines every app used to keep in sync by hand. GET /v1/platform/projects/{id}/vars and GET /v1/platform/environments/{id}/vars return every variable name, which ones are secret, and the non-secret values (sensitive-looking ones masked, the house rule for variables). Secret values are never returned. Each read also carries the group's revision, a counter that goes up on every write.
PUT on the same paths takes {"vars": {...}, "secrets": {...}} and replaces the whole group — a partial edit would be a read-modify-write race, so the honest unit is the group. vars are stored readable; secrets are sealed (AES-GCM, bound to the group) and come back as names only. Each name goes in exactly one of the two maps. Platform-managed names — DOMAIN, HOST_PORT, DATABASE_URL and the other routing/resource keys — are refused, as are duplicates. Limits: 100 names per group, 32 KiB per value, 128 KiB total. Sending empty vars and secrets deletes the group: an empty scope is the honest "no group".
Values follow the same rules as an app's own variables: one line of valid UTF-8 text, at most 32 KiB, with no control characters, and no $ or # — the environment file an app starts from is read verbatim, where $ triggers variable substitution and # starts a comment. Names starting with COMPOSE_ or DOCKER_ are refused, because they steer the container engine itself. A value kept with __IMPREZA_KEEP__ answers to the same rules.
Send base_revision with the revision you read to refuse overwriting a change you have not seen: if the group was written since, the request fails with 409 REVISION_CONFLICT and nothing is written. Without base_revision the write replaces the group as before. To keep a stored value without typing it again, send the exact value __IMPREZA_KEEP__ for that name, in the map it is stored in (secrets for a secret, vars for a masked plain value): the stored value is put back, and a secret stays sealed and is never returned. A marker for a name that has no stored value, or in the other map, fails with 409 KEEP_TARGET_MISSING, so the marker itself is never saved as a value. The limits above apply to the values actually stored, the kept ones included: a group that would exceed them is refused with 400, and nothing changes.
Resolution is project → environment → app on each app's next deploy, the narrower level winning per name. It applies to an ordinary redeploy and to every stage of the ordered environment deploy below. Group values merge into the deploy payload only — they are never written into the app's own stored variables — and running containers do not change. With MCP, impreza_get_variable_group takes project_id plus an optional environment_id; impreza_set_variable_group takes the same identifiers plus vars and secrets.
In the portal, choose the scope (project or environment) under Variable groups and click Load group. Plain values can be edited in place. A saved secret, like a plain value masked by the rule above, is listed by name with an empty field: leave it blank to keep the stored value, type a new value to replace it, or remove the row to delete it. The page never receives a secret value. Save complete group replaces the whole group after you type the project or environment ID. The page saves against the revision it loaded: if someone else saved the group in the meantime, the save is refused and nothing changes, so load the group again and redo your edit.
Environment wiring
impreza_prepare_service_binding (and POST /v1/platform/deployments/custom/{id}/prepare-binding) accepts an optional wire: true. The review names the sibling components it would wire (wire_components). On apply, every other eligible web/worker component of the same environment receives its own managed connection to the same provider: its own binding reference, its own generated credential, its own deploy job — never a share of the first consumer's credential. Eligibility is rechecked inside the apply transaction: components already bound, busy or with an incompatible shape are skipped and named in the receipt, never a failure.
Wiring also applies on its own inside the ordered deploy below: when a web/worker component runs as a stage and its manifest carries no binding reference, the stage manifest inherits the reference of the one active binding its environment holds for it. The reference only — the credential stays on the agent-only path, exactly like the primary connection. An explicit binding already in the manifest always wins, a retiring binding is never injected, and the provider must live in the same environment with a database or cache role. An ordinary redeploy simply reuses the recorded manifest, so whatever reference a reviewed connection wrote there is what keeps serving.
Ordered environment deploy
POST /v1/platform/environments/{id}/deploy stages every non-preview service of the environment in role order — database → cache → worker → web — and returns 202 with a batch_id (edb_...); GET /v1/platform/environment-deploys/{id} reports the stages, the cursor and the terminal state. GET /v1/platform/environments/{id}/deploys lists the environment's 20 most recent batches, newest first, with each batch's batch_id, status, require_healthy, cursor, stage_count, error, created_at and finished_at (read scope). Each stage is an ordinary smart-replace deploy, so variable groups resolve and wiring applies per stage, and each stage sees the state that exists when it runs, not when the batch was created.
Stages are gated on health by default: require_healthy_start defaults to true, every stage's manifest gains the existing startup-health requirement, and every stage's agent must already announce startup-health-v1 — checked before anything queues, because refusing early beats a batch that dies at stage two. Pass require_healthy_start: false to deploy without the gate. One running batch per environment; every component must be settled (running or failed, no lifecycle operation in flight).
A failed stage fails the batch and the batch names the component that stopped the line. Nothing rolls back implicitly: the stages that already succeeded stay deployed, and the fix is another deploy — the same honesty the single-app operations have. MCP: impreza_deploy_environment (environment_id, optional require_healthy_start) impreza_get_environment_deploy (batch_id) and impreza_list_environment_deploys (environment_id).
In the portal, Deploy environment in order starts the batch after you type the environment name. Require healthy start stays on unless you clear it. The page lists the environment's recent batches and follows each stage of the one in progress until it ends. Reloading the page or closing the section never stops the batch: choose the environment again and the page resumes following it.
Configuration promotion
Image promotion moves the immutable artifact; a configuration promotion moves the configuration around it between two environments of one project. POST /v1/platform/environments/{id}/config-promotions with {"source_environment_id": "env_..."} prepares a 15-minute review (cpro_...) of what the target environment would inherit: the environment variable group and each paired component's user variables, components paired by name exactly like image promotion. GET /v1/platform/config-promotions/{id} reads the saved review; POST /v1/platform/config-promotions/{id}/apply with the exact review_digest and confirm: true applies it.
The same machinery as every other reviewed mutation: sealed payload, digest over the ciphertext, one receipt, replay returns the receipt, at most 20 pending reviews per account. The diff buckets names only — same, different, only in source, only in target — never a value. Secret group values travel inside the sealed review and are re-sealed under the target group on apply; the source group is never rewritten.
Applying writes the target environment's variable group and replaces each paired component's user variables with the source's (platform-managed keys are preserved). It does not restart or redeploy anything: the configuration takes effect on each app's next deploy, and the ordered environment deploy above is the way to roll it out. MCP: impreza_prepare_config_promotion (target_environment_id, source_environment_id), impreza_get_config_promotion (promotion_id) and impreza_apply_config_promotion (promotion_id, review_digest, confirm: true).
In the portal, Promote configuration takes a source and a target environment of the same project and prepares the review. The review shows the name buckets and the paired components, never a value. Apply reviewed configuration applies that exact review after you type the target environment name. When both environments already match, the portal says there is nothing to review.
Rename and delete
POST /v1/platform/projects/{id}/rename and /v1/platform/environments/{id}/rename take {"name": "..."} (deploy scope) and change organization only — nothing about the workload. POST .../delete (manage scope) requires confirm set to the current name, verbatim: organization deletion is not the two-step review a deploy gets, but a stray confirm: true must never be enough to remove the label a team's services hang under. A project can be deleted only once every environment inside it is deleted; an environment only once every service is detached. Variable groups under the target go with it, and their sealed secrets die there. No application is stopped or uninstalled — a deletable project or environment is already empty. MCP: impreza_rename_project, impreza_delete_project, impreza_rename_environment, impreza_delete_environment.
Previews in an environment
A dark preview of an attached application joins the same environment automatically, with the role preview and a component named after its parent plus a short branch digest. The inventory can then say what is a review artifact, and the machinery above ignores previews by construction: they are never a stage of the ordered deploy, never wired and never paired by a configuration promotion. The membership dies when the preview is retired; the application and its data are otherwise untouched.