---
title: "Create a deployment"
method: POST
path: "/v1/apps/{appId}/deployments"
tags: ["Deployments"]
deprecated: true
---

# Create a deployment

`POST /v1/apps/{appId}/deployments`

## Request

- Base URL: `https://api.spice.ai`
- URL: `https://api.spice.ai/v1/apps/{appId}/deployments`
- Auth: HTTP bearer

> **Deprecated.**

Legacy alias of `/v1/projects/{projectId}/deployments`. Projects were previously called apps. Creates a new deployment for the specified project using its current spicepod configuration. A project connected to a GitHub repository deploys spicepod.yaml from its root directory, read when the deployment is created. It reads the head of the production branch, or the `branch` or `commit_sha` the request names. The runtime image is selected based on the project's update channel.

## Path parameters

- `appId` integer, required

## Headers

- `X-Org-Name` string

## Request body

- object
  - `image_tag` string: Override the runtime image tag for this deployment. Requires the Enterprise plan; omit it to deploy the image the project is configured with. When the effective update channel is `stable` or `preview`, a supplied tag must be a published version for that channel. When the effective update channel is `nightly`, a supplied tag must be a published `spicehq/spiceai` `nightly-*` tag (`YYYYMMDDHHmm` plus `-` plus 7 to 40 lowercase hex characters, for example `202609081124-cadf292`).
  - `channel` 'stable' | 'preview' | 'nightly': Update channel that determines the runtime image
  - `expected_update_channel` 'stable' | 'preview' | 'nightly': If set, the stored project update channel must match this value or the request is refused with 409 `update_channel_mismatch`. This is a precondition, not an override (`channel` still changes the image train).
  - `replicas` integer: Override the number of replicas for this deployment
  - `branch` string: For a project connected to a GitHub repository, deploy spicepod.yaml from the head of this branch or tag of that repository instead of the production branch. The branch or tag must exist. With `commit_sha`, it must contain the commit. The production branch and the stored spicepod do not change, so the next deployment without a revision, and any automatic redeploy, deploy the production branch head again. A project with no connected repository refuses this field with 400 `repository_not_connected`. A project whose org has no GitHub App installation refuses it with 422 `github_installation_missing`. A request from the deploy GitHub Action (User-Agent `spice-cloud-deploy-action`) does not check or use this field for any project, because the Action sends the branch of its workflow run as metadata.
  - `commit_sha` string: For a project connected to a GitHub repository, deploy spicepod.yaml from this commit instead of the head of the production branch. The commit must be on `branch`, or on the production branch (never a tag of the same name) when `branch` is omitted, so a commit that only a fork holds is refused. The deployment records the full SHA. The production branch and the stored spicepod do not change. A project with no connected repository refuses this field with 400 `repository_not_connected`. A project whose org has no GitHub App installation refuses it with 422 `github_installation_missing`. A request from the deploy GitHub Action (User-Agent `spice-cloud-deploy-action`) does not check or use this field for any project, because the Action sends the commit of its workflow run as metadata.
  - `commit_message` string: Not used. A deployment from a connected repository records the message of the commit it reads.
  - `debug` boolean: Enable debug mode for this deployment
  - `target` object: Optional per-deployment override. Omit (or `{ kind: cloud }`) to place the deployment where the project itself runs: Spice-managed compute, its own BYOC cluster, or its attached standalone Cloud Connect instance. That placement is a property of the project, not of this request. Pass `{ kind: cluster, instance_id }` to override that for this deployment and route it to a BYOC cluster connection; the cluster must be owned by the token's org, not in a terminal lifecycle state (decommissioning/archived), not paused, and the operator must hold a live connection (verified by the connectivity ping, not the Supabase lifecycle status). A standalone project cannot be overridden this way and is refused with 409.
    - `kind` 'cloud' | 'cluster'
    - `instance_id` string: Required when kind is `cluster`.

## Response `202`

Deployment created and queued for processing

- Deployment
  - `id` integer: Unique identifier for the deployment
  - `status` 'queued' | 'in_progress' | 'succeeded' | 'failed' | 'created': Current status of the deployment
  - `created_at` string, date-time: Timestamp when the deployment was created
  - `updated_at` string, date-time, nullable: Timestamp when the deployment was last updated
  - `image_tag` string, nullable: Runtime image tag used for this deployment
  - `replicas` integer: Number of replicas
  - `branch` string, nullable: Git branch name
  - `commit_sha` string, nullable: Git commit SHA
  - `commit_message` string, nullable: Git commit message
  - `error_code` string, nullable: Set with error_message on failed and on in_progress while Cloud is retrying a scheduling block. Null on succeeded, on ordinary waiting in_progress, and on writers that predated this column. Older failed rows may still have a message with a null code.
  - `error_message` string, nullable: Catalog copy for the current error_code. Older failed rows may still have a message with a null error_code.
  - `creation_source` string, nullable: Source that triggered the deployment
  - `created_by` integer, nullable: User ID who created the deployment

## Other responses

- `400`: Invalid request body, project has no spicepod configuration, the spicepod has been deleted, or the spicepod is paused (`code: spicepod_paused`; resume it with POST /v1/projects/{projectId}/resume before deploying). Also returned when the spicepod `name`, `kind` or `version` is missing or not valid: the body is `{ error: 'Invalid spicepod configuration' }` with an `issues` array of `{path, message}`. The runtime checks all other spicepod fields when it loads the spicepod. For a standalone Cloud Connect project, also returned when the Spicepod references a `${secrets:...}` name the project does not define: the body carries `code: unknown_secret_references`, a `missing_secrets` array of `{name, path, suggestion?}`, and `secrets_url`. When the effective update channel is `stable` or `preview` and `image_tag` is supplied, the tag must be a published version for that channel or the body is `{ error: 'Invalid image_tag for stable channel', code: 'invalid_stable_image_tag' }` (or the preview analog). When the effective update channel is `nightly` and `image_tag` is supplied, the tag must be a published `spicehq/spiceai` `nightly-*` tag of the form `YYYYMMDDHHmm` plus `-` plus 7 to 40 lowercase hex characters (for example `202609081124-cadf292`) or the body is `{ error: 'Invalid image_tag for nightly channel', code: 'invalid_nightly_image_tag' }`. For a project connected to a GitHub repository, also returned when the root directory has no spicepod.yaml or it is invalid or too large (`code: spicepod_not_found`, `spicepod_invalid` or `spicepod_too_large`), when `commit_sha` is not on `branch` (or on the production branch when `branch` is omitted) (`code: commit_not_on_branch`), or when `commit_sha` comes without `branch` and the project has no production branch (`code: branch_required`). Nothing is dispatched and no deployment is recorded. Also returned when `branch` or `commit_sha` is sent for a project with no connected repository (`code: repository_not_connected`), except from the deploy GitHub Action. Nothing is dispatched.
- `401`: Unauthorized
- `403`: Insufficient scope, viewer role cannot create deployments, `image_tag` was supplied outside the Enterprise plan (code: `image_tag_requires_enterprise`), the requested runtime image is not available for the organization plan (code: `image_tag_not_allowed_for_plan`), or the organization's GitHub App installation cannot read the connected repository (code: `github_unauthorized` or `github_forbidden`); omitting `image_tag` deploys the image the project is configured with
- `404`: Project not found. For a `cluster` target, also returned when the BYOC cluster connection does not exist or is not owned by the token's org. Also returned when the connected GitHub repository, its production branch, or the requested `branch` or `commit_sha` was not found (`code: github_not_found`). With `commit_sha` and no `branch`, this includes a production branch that no longer exists.
- `409`: A deployment is already in progress for this project (`code: deployment_in_progress`, with optional `activeDeploymentId`). For a `cluster` target, also returned when the BYOC cluster is paused, offline, or otherwise not in a deployable state. Also returned when the project's deploy target is unusable: its kind conflicts with its cluster/instance references, a `cluster` target was requested for a standalone project, a standalone project has no attached instance, or that instance has not yet enrolled the encryption key its secrets are sealed to (retrying once it is online succeeds). Also returned when `expected_update_channel` does not match the stored spicepod update channel, or when that channel changes before dispatch (`code: update_channel_mismatch`). Also returned when the project is paused after the request checked it (`code: spicepod_paused`), or its spicepod is deleted after that check (`code: spicepod_deleted`). Also returned when the project's repository, root directory, or production branch changes while the spicepod is read from the repository; deploy again. Nothing is dispatched and no deployment is recorded.
- `413`: Request body exceeds the maximum size (1 MiB), or, for a standalone Cloud Connect project, the project's resolved secrets exceed the maximum sealed-payload size. Nothing is dispatched and no deployment is recorded.
- `422`: Organization is not fully configured, no runtime version in the catalog matches this project’s version range (`code: version_no_match`), or the spicepod uses version v1beta1, which the Spice 2 runtime cannot load (`code: spicepod_version_unsupported`). Also returned when the Spicepod uses a connection credential that holds a secret reference deploy cannot resolve: the body carries `code: connection_credentials_unresolved` and an `unresolved_credentials` array of `{connection, field}`. Nothing is deployed. Also returned when `branch` or `commit_sha` is sent for a project with a connected repository whose org has no GitHub App installation to read it (`code: github_installation_missing`), except from the deploy GitHub Action. Nothing is dispatched.
- `429`: GitHub rate-limited the read of the connected repository (`code: github_rate_limited`).
- `500`: Internal server error. For a standalone Cloud Connect project, also returned with `code: deployment_not_settled` when the deployment record could not be moved out of `queued`; the project's next deployment is refused until it is cleared.
- `502`: Upstream service failure while provisioning the deployment, or, for a standalone Cloud Connect project, the spicepod could not be handed to the instance's gateway, or the instance rejected it. Also returned when GitHub did not answer while the connected repository was read (`code: github_unavailable`).
- `503`: The runtime version catalog is unavailable or empty (`code: version_source_unavailable`), so an unpinned stable or preview deploy cannot resolve an image tag, or catalog membership for an explicit `image_tag` override cannot be validated. Also returned when the published nightly tag list cannot be loaded to validate a nightly `image_tag` override.

---

[API](https://skmtc.dev/spice/apis/spice-cloud-control-plane-api.md) - [All operations](https://skmtc.dev/spice/apis/spice-cloud-control-plane-api/llms.txt) - [OpenAPI document](https://skmtc.dev/spice/apis/spice-cloud-control-plane-api/revisions/5f3b75dd847d?raw)
