Manage deployments
List, inspect, retry, unpromote, delete, and clean up deployments.
List deployments
curl "$API_BASE/v1/deployments?page=1&limit=20" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.deployments.get_deployments(params={"page": 1, "limit": 20})
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.deployments.getDeployments({ params: { page: 1, limit: 20 } });
{
"deployments": [
{
"promotion_id": "7d3f92a1-…",
"pipeline_id": "9f2c41d8-…",
"model_id": "9f2c41d8-…",
"model_name": "Daily sales forecaster",
"workspace_id": "ws_3fa8…",
"training_id": "b81e57c3-…",
"deployment_type": "shared",
"status": "ready",
"endpoint": "…/v1/models/model-b81e57c3",
"inference_endpoint": null,
"ready": true,
"live_status": "ready",
"promoted_by": "usr_91c2…",
"promoted_at": "2026-07-15T12:04:31Z",
"deployed_at": "2026-07-15T12:06:02Z",
"last_used_at": "2026-07-15T14:41:19Z",
"inference_count": 412,
"reason": "Beats current champion by 4 points",
"namespace": null,
"resource_tags": {},
"deployment_config": {},
"is_foundation_direct": false,
"foundation_family": null,
"foundation_slug": null,
"foundation_adapter_id": null
}
],
"total": 4,
"page": 1,
"pages": 1,
"showing": 4
}Sorted newest first.
Query parameters
| Parameter | Meaning |
|---|---|
status optional | Filter by lifecycle status. |
pipeline_id optional | Only deployments of one pipeline. (model_id is a deprecated alias.) |
page optional | Page number. Defaults to 1. |
limit optional | Items per page. Defaults to 20. |
include_archived optional | Include force-deleted (archived) records. Defaults to false. |
Archived deployments are hidden by default; filtering
status=archived without include_archived=true returns an empty list.
Response fields
| Field | Meaning |
|---|---|
promotion_id | The deployment's ID — what you pass to inspect, retry, and delete. |
pipeline_id / model_name | The pipeline this deployment serves, joined for display. |
training_id | The specific training being served. null for foundation-direct deployments. |
deployment_type | shared (multi-tenant pool), dedicated (reserved pod), or foundation (foundation servers). |
status | Lifecycle status, resolved live — terminal states (failed, inactive, archived) always win over cluster snapshots. |
ready / live_status | Whether the deployment can serve right now, and the raw cluster view behind it. |
endpoint / inference_endpoint | Where to send inference requests. |
promoted_by / promoted_at / deployed_at / last_used_at | Who deployed it, when it was created, when serving came up, and when it last answered a request. |
inference_count | Total requests served. |
reason | The free-text note from the deploy call. |
is_foundation_direct + foundation_* | Foundation-model deployments set is_foundation_direct: true and describe their model in foundation_family / foundation_slug / foundation_adapter_id instead of referencing a training. |
Inspect one deployment
curl "$API_BASE/v1/deployments/$PROMOTION_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.deployments.get_deployments_by_promotion_id(PROMOTION_ID)
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.deployments.getDeploymentsByPromotionId(PROMOTION_ID);
{
"deployment": {
"promotion_id": "7d3f92a1-…",
"model_id": "9f2c41d8-…",
"model_name": "Daily sales forecaster",
"workspace_id": "ws_3fa8…",
"training_id": "b81e57c3-…",
"training_accuracy": 0.94,
"deployment_type": "shared",
"status": "ready",
"endpoint": "…/v1/models/model-b81e57c3",
"inference_endpoint": null,
"ready": true,
"live_status": "ready",
"promoted_by": "usr_91c2…",
"promoted_at": "2026-07-15T12:04:31Z",
"deployed_at": "2026-07-15T12:06:02Z",
"deployment_started_at": "2026-07-15T12:04:32Z",
"last_used_at": "2026-07-15T14:41:19Z",
"last_health_check": "2026-07-15T14:45:00Z",
"inference_count": 412,
"reason": "Beats current champion by 4 points",
"resource_tags": {},
"deployment_config": {},
"is_foundation_direct": false,
"foundation_family": null,
"foundation_slug": null,
"foundation_adapter_id": null
}
}The detail adds training_accuracy (the promoted training's score) and
the health timestamps. status and ready are resolved live at read
time, so this is the truth about whether the deployment can serve right
now. A 404 means the ID doesn't exist or the record was archived.
Retry a failed deployment
A deployment in terminal failed status can be retried in place:
curl -X POST "$API_BASE/v1/deployments/$PROMOTION_ID/retry" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.deployments.post_deployments_by_promotion_id_retry(PROMOTION_ID)
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.deployments.postDeploymentsByPromotionIdRetry(PROMOTION_ID);
{
"success": true,
"promotion_id": "7d3f92a1-…",
"status": "deploying",
"retry_count": 1,
"message": "Deployment retry started"
}The retry re-runs the deployment on the same record — same
promotion_id, same training. Anything you've stored that points at the
deployment stays valid, and the audit history stays on one record instead
of fragmenting across a delete-and-repromote. No request body needed.
Errors
| Status | Code | Why |
|---|---|---|
404 | NOT_FOUND | No such deployment. |
403 | FORBIDDEN | Wrong workspace, no pipeline access, or your organization role can't deploy. |
409 | RETRY_FAILED | The deployment isn't in failed status — retries on healthy or in-flight deployments are rejected. |
400 | RETRY_FAILED | The record can't be retried; the message says why. |
Unpromote a pipeline
The graceful way to take a model out of serving. Without a body it
removes all of the pipeline's active deployments; pass training_id to
remove only one:
curl -X POST "$API_BASE/v1/deployments/pipelines/$PIPELINE_ID/unpromote" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{ "training_id": "'$TRAINING_ID'" }'from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.deployments.post_deployments_pipelines_by_pipeline_id_unpromote(PIPELINE_ID, json={"training_id": TRAINING_ID})
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.deployments.postDeploymentsPipelinesByPipelineIdUnpromote(PIPELINE_ID, { json: { training_id: TRAINING_ID } });
{
"success": true,
"pipeline_id": "9f2c41d8-…",
"message": "Model unpromoted successfully",
"removed_deployments": ["7d3f92a1-…"]
}Request body
| Field | Meaning |
|---|---|
training_id optional | Remove only the deployment serving this training. Omit to remove all of the pipeline's active deployments. |
Records survive with status inactive, so listings and history keep
working. A 400 with code UNPROMOTION_FAILED means nothing was
removed — typically because the pipeline wasn't serving.
Delete a deployment
Force-delete one deployment: serving resources are torn down and the record is archived in the same call, regardless of its state.
curl -X DELETE "$API_BASE/v1/deployments/$PROMOTION_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.deployments.delete_deployments_by_promotion_id(PROMOTION_ID)
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.deployments.deleteDeploymentsByPromotionId(PROMOTION_ID);
{
"success": true,
"promotion_id": "7d3f92a1-…",
"message": "Deployment deleted successfully"
}Reach for delete when a deployment is stuck mid-deploy or left behind
after a failure. For routine rollover, prefer
unpromote — it keeps the record active in your
history. A 400 with code DELETION_FAILED means neither the record nor
the serving resources could be removed; try again.
Clean up stale deployments
Sweep idle and failed deployments across the workspace in one call. Requires an organization admin role (or account-level administrator).
curl -X POST "$API_BASE/v1/deployments/cleanup" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{ "type": "all" }'from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.deployments.post_deployments_cleanup(json={"type": "all"})
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.deployments.postDeploymentsCleanup({ json: { type: "all" } });
{
"success": true,
"cleanup_results": {
"shared": { "cleaned": 2 },
"dedicated": { "cleaned": 0 }
},
"message": "Cleanup completed"
}Request body
| Field | Meaning |
|---|---|
type optional | What to sweep: "shared", "dedicated", or "all" (the default). |
A 403 with code FORBIDDEN means your role can't run cleanup.

