# Fork a project

## `POST /v1/projects/{projectId}/forks`

Creates a new project from this project's configuration and records this project as its source. The fork copies the spicepod the project deploys, its linked connections, project secrets, linked organization secrets, update channel, version range, replica and executor counts, storage size, description, and tags. It does not copy API keys (new ones are generated), deployments, the GitHub repository link, the instance size, or visibility (forks are private). The fork is not deployed: deploy it with POST /v1/projects/{projectId}/deployments. Requires `apps:write`. The server copies secret values without returning them, so no secrets scope is needed. A source connected to a GitHub repository is read from that repository, and its spicepod is copied only when it loads no other file from the repository; to deploy a fork from a fork of the repository, fork the project in the Spice portal.

### Path parameters

- `projectId` (integer, required): The ID of the project to fork

### Header parameters

- `Authorization` (string, required): `Bearer <token>`. [Authentication](https://spice.ai/docs/cloud/api/management#authentication) lists the tokens it accepts.
- `X-Org-Name` (string, optional): The organization to act on, by handle. Omit it to act on the organization the credential is bound to; sending it blank is a 400, not the same as omitting it. A user credential may name any organization its owner belongs to; a machine credential is pinned to its own. A portal session token is bound to none, so it must send this header on every route but `GET /v1/orgs` (400 `org_assertion_required` otherwise). Pattern: `^[A-Za-z0-9._-]{1,255}$`.

### Request body

Optional.

Content type: `application/json`

- `name` (string): Name of the fork. Defaults to the source name with `-fork`, then `-fork-2` and so on, whichever is free. Minimum length: `4`. Maximum length: `38`. Pattern: `^[a-zA-Z0-9-]+$`.
- `region` (string): Region to place the fork in. Omit it and `cluster_name` to place the fork where the source runs. Allowed values: `us-east-1`, `us-west-2`.
- `cluster_name` (string): Dedicated cluster to place the fork on, as listed by `GET /v1/clusters`. If `region` is also set, it must match the cluster's region.
- `scheduler_state_location` (string): s3:// URI for the fork's scheduler state. Required when the source spicepod sets `runtime.scheduler.state_location`, and must not overlap it: two schedulers cannot share a location. Maximum length: `2048`.
- `default_alerts` (boolean): Create default monitoring alerts for the new managed fork. False skips initial setup. Manual alert creation remains available. Default: `true`.

### Example request

```bash
curl -X POST "https://api.spice.ai/v1/projects/<projectId>/forks" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "string",
    "region": "us-east-1",
    "cluster_name": "string",
    "scheduler_state_location": "string",
    "default_alerts": true
  }'
```

```http
POST /v1/projects/<projectId>/forks HTTP/1.1
Host: api.spice.ai
Authorization: Bearer <token>
Content-Type: application/json
Content-Length: 141

{
  "name": "string",
  "region": "us-east-1",
  "cluster_name": "string",
  "scheduler_state_location": "string",
  "default_alerts": true
}
```

### Responses

**201 Created**

Project forked. It has not been deployed yet.

Content type: `application/json`

- `id` (integer): Unique identifier for the project
- `name` (string): Name of the project
- `kind` (string): Resolved project hosting model. Allowed values: `managed`, `standalone`, `byoc`.
- `description` (string | null): Description of the project
- `visibility` (string): Visibility setting for the project. Allowed values: `public`, `private`.
- `created_at` (string (date-time)): Timestamp when the project was created
- `cname` (string | null): Region identifier
- `cluster_name` (string | null): Resolved dedicated cluster name; null for projects not assigned to a dedicated cluster.
- `endpoint` (string | null): The data-plane endpoint the project connects to, or null before a standalone instance is attached.
- `production_branch` (string | null): Production branch for the project
- `api_key` (string, deprecated): Deprecated. Use the API Keys endpoints instead.
- `tags` (map of string | null): Key-value tags for the project
- `forked_from` (object | null): The project this project was forked from, while that project is live in the same organization. Null for a project that was not created as a fork.
  - `id` (integer, required): ID of the source project
  - `name` (string, required): Name of the source project
- `paused_at` (string (date-time) | null): Timestamp when the project was paused; null while running
- `warning` (string | null): Present when the project was created but initial configuration could not be applied
- `config` (object | null): Runtime configuration; null for an unattached standalone project.
  - `spicepod` (object | null): Spicepod configuration
  - `registry` (string | null): Container registry for the runtime image
  - `image_tag` (string | null): Runtime image tag
  - `update_channel` (string | null): Update channel for the runtime. Allowed values: `stable`, `preview`, `nightly`.
  - `replicas` (integer): Number of replicas
  - `resources` (object | null): Resource requests and limits for the project container
    - `limits` (object)
      - `cpu` (string): Whole-number vCPU limit, or '-' for no CPU limit
      - `memory` (string): Memory limit in Gi (for example, 16Gi)
      - `ephemeral-storage` (string)
    - `requests` (object)
      - `cpu` (string): CPU request. Minimum 100 millicores (for example, 100m or 0.1).
      - `memory` (string): Memory request. Minimum 192Mi.
  - `executor` (object | null): Executor container configuration
    - `replicas` (integer): Number of executor replicas
    - `resources` (object): Resource requests and limits for the executor container
      - `limits` (object)
        - `cpu` (string): Whole-number vCPU limit, or '-' for no CPU limit
        - `memory` (string): Memory limit in Gi (for example, 16Gi)
        - `ephemeral-storage` (string)
      - `requests` (object)
        - `cpu` (string): CPU request. Minimum 100 millicores (for example, 100m or 0.1).
        - `memory` (string): Memory request. Minimum 192Mi.
  - `region` (string | null): Cloud region code (e.g., us-east-1)
  - `node_group` (string | null): Node group for scheduling the project runtime
  - `storage_claim_size_gb` (number | null): Storage claim size in GB
- `shared_state` (array of object, required): Settings copied as they are that name external state the source also uses: a replication slot or Kafka consumer group the spicepod names, or a snapshot location. Change them in the fork's spicepod before deploying it if the two projects should not share that state.
  - `component` (string, required): The dataset, or `snapshots`
  - `setting` (string, required): Example: `pg_replication_slot`.
  - `value` (string, required)

**400 Bad Request**

Invalid project ID or request body, an unknown region or cluster, no `region` or `cluster_name` set for a source whose placement a fork cannot reuse (a BYOC cluster, a self-hosted runtime, or a region or cluster that is no longer available; `code: fork_placement_required`), or a missing or unusable `scheduler_state_location` (`code: scheduler_state_location_required`, `scheduler_state_location_unsupported`, `scheduler_state_location_overlaps_source`, or `scheduler_state_location_not_applicable`).

**401 Unauthorized**

Unauthorized

**402 Payment Required**

The source runs models on hosted AI credits, which the organization has used up (`code: ai_credits_exhausted`).

**403 Forbidden**

Insufficient scope, a viewer role, or the source runs more replicas or executors than the plan or the dedicated cluster allows

**404 Not Found**

Project or organization not found

**409 Conflict**

A project with the requested name already exists, another project creation is in progress, or every default name is taken (`code: fork_name_unavailable`)

**413 Content Too Large**

Request body too large

**422 Unprocessable Content**

The source has no spicepod (`code: fork_source_has_no_spicepod`), its spicepod is not valid (`code: fork_source_spicepod_invalid`), or its spicepod loads files from its GitHub repository besides spicepod.yaml (`code: fork_source_loads_repository_files`)

**429 Too Many Requests**

Too many project creation requests (rate limited)

**500 Internal Server Error**

The fork could not be created. When the failure comes after the project row is created, the fork is deleted again with its spicepod, secrets, and organization secret links. If the project itself cannot be deleted, the response has `code: fork_rollback_failed` and names the partially created project in `project` (`id` and `name`). Delete that project, by `project.id`, before reusing its name.

**502 Bad Gateway**

The source is connected to a GitHub repository, and its spicepod could not be read from it (`code: fork_source_repository_unreadable`)

## `POST /v1/orgs/{orgId}/projects/{projectId}/forks`

The org is `orgId` in the path. Creates a new project from this project's configuration and records this project as its source. The fork copies the spicepod the project deploys, its linked connections, project secrets, linked organization secrets, update channel, version range, replica and executor counts, storage size, description, and tags. It does not copy API keys (new ones are generated), deployments, the GitHub repository link, the instance size, or visibility (forks are private). The fork is not deployed: deploy it with POST /v1/projects/{projectId}/deployments. Requires `apps:write`. The server copies secret values without returning them, so no secrets scope is needed. A source connected to a GitHub repository is read from that repository, and its spicepod is copied only when it loads no other file from the repository; to deploy a fork from a fork of the repository, fork the project in the Spice portal.

### Path parameters

- `orgId` (integer, required): The org id from GET /v1/orgs. Minimum: `1`.
- `projectId` (integer, required): The ID of the project to fork

### Header parameters

- `Authorization` (string, required): `Bearer <token>`. [Authentication](https://spice.ai/docs/cloud/api/management#authentication) lists the tokens it accepts.
- `X-Org-Name` (string, optional): When present, X-Org-Name must name the same org as orgId. Otherwise the request returns org\_path\_mismatch (400). Pattern: `^[A-Za-z0-9._-]{1,255}$`.

### Request body

Optional.

Content type: `application/json`

- `name` (string): Name of the fork. Defaults to the source name with `-fork`, then `-fork-2` and so on, whichever is free. Minimum length: `4`. Maximum length: `38`. Pattern: `^[a-zA-Z0-9-]+$`.
- `region` (string): Region to place the fork in. Omit it and `cluster_name` to place the fork where the source runs. Allowed values: `us-east-1`, `us-west-2`.
- `cluster_name` (string): Dedicated cluster to place the fork on, as listed by `GET /v1/clusters`. If `region` is also set, it must match the cluster's region.
- `scheduler_state_location` (string): s3:// URI for the fork's scheduler state. Required when the source spicepod sets `runtime.scheduler.state_location`, and must not overlap it: two schedulers cannot share a location. Maximum length: `2048`.
- `default_alerts` (boolean): Create default monitoring alerts for the new managed fork. False skips initial setup. Manual alert creation remains available. Default: `true`.

### Example request

```bash
curl -X POST "https://api.spice.ai/v1/orgs/<orgId>/projects/<projectId>/forks" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "string",
    "region": "us-east-1",
    "cluster_name": "string",
    "scheduler_state_location": "string",
    "default_alerts": true
  }'
```

```http
POST /v1/orgs/<orgId>/projects/<projectId>/forks HTTP/1.1
Host: api.spice.ai
Authorization: Bearer <token>
Content-Type: application/json
Content-Length: 141

{
  "name": "string",
  "region": "us-east-1",
  "cluster_name": "string",
  "scheduler_state_location": "string",
  "default_alerts": true
}
```

### Responses

**201 Created**

Project forked. It has not been deployed yet.

Content type: `application/json`

- `id` (integer): Unique identifier for the project
- `name` (string): Name of the project
- `kind` (string): Resolved project hosting model. Allowed values: `managed`, `standalone`, `byoc`.
- `description` (string | null): Description of the project
- `visibility` (string): Visibility setting for the project. Allowed values: `public`, `private`.
- `created_at` (string (date-time)): Timestamp when the project was created
- `cname` (string | null): Region identifier
- `cluster_name` (string | null): Resolved dedicated cluster name; null for projects not assigned to a dedicated cluster.
- `endpoint` (string | null): The data-plane endpoint the project connects to, or null before a standalone instance is attached.
- `production_branch` (string | null): Production branch for the project
- `api_key` (string, deprecated): Deprecated. Use the API Keys endpoints instead.
- `tags` (map of string | null): Key-value tags for the project
- `forked_from` (object | null): The project this project was forked from, while that project is live in the same organization. Null for a project that was not created as a fork.
  - `id` (integer, required): ID of the source project
  - `name` (string, required): Name of the source project
- `paused_at` (string (date-time) | null): Timestamp when the project was paused; null while running
- `warning` (string | null): Present when the project was created but initial configuration could not be applied
- `config` (object | null): Runtime configuration; null for an unattached standalone project.
  - `spicepod` (object | null): Spicepod configuration
  - `registry` (string | null): Container registry for the runtime image
  - `image_tag` (string | null): Runtime image tag
  - `update_channel` (string | null): Update channel for the runtime. Allowed values: `stable`, `preview`, `nightly`.
  - `replicas` (integer): Number of replicas
  - `resources` (object | null): Resource requests and limits for the project container
    - `limits` (object)
      - `cpu` (string): Whole-number vCPU limit, or '-' for no CPU limit
      - `memory` (string): Memory limit in Gi (for example, 16Gi)
      - `ephemeral-storage` (string)
    - `requests` (object)
      - `cpu` (string): CPU request. Minimum 100 millicores (for example, 100m or 0.1).
      - `memory` (string): Memory request. Minimum 192Mi.
  - `executor` (object | null): Executor container configuration
    - `replicas` (integer): Number of executor replicas
    - `resources` (object): Resource requests and limits for the executor container
      - `limits` (object)
        - `cpu` (string): Whole-number vCPU limit, or '-' for no CPU limit
        - `memory` (string): Memory limit in Gi (for example, 16Gi)
        - `ephemeral-storage` (string)
      - `requests` (object)
        - `cpu` (string): CPU request. Minimum 100 millicores (for example, 100m or 0.1).
        - `memory` (string): Memory request. Minimum 192Mi.
  - `region` (string | null): Cloud region code (e.g., us-east-1)
  - `node_group` (string | null): Node group for scheduling the project runtime
  - `storage_claim_size_gb` (number | null): Storage claim size in GB
- `shared_state` (array of object, required): Settings copied as they are that name external state the source also uses: a replication slot or Kafka consumer group the spicepod names, or a snapshot location. Change them in the fork's spicepod before deploying it if the two projects should not share that state.
  - `component` (string, required): The dataset, or `snapshots`
  - `setting` (string, required): Example: `pg_replication_slot`.
  - `value` (string, required)

**400 Bad Request**

Invalid project ID or request body, an unknown region or cluster, no `region` or `cluster_name` set for a source whose placement a fork cannot reuse (a BYOC cluster, a self-hosted runtime, or a region or cluster that is no longer available; `code: fork_placement_required`), or a missing or unusable `scheduler_state_location` (`code: scheduler_state_location_required`, `scheduler_state_location_unsupported`, `scheduler_state_location_overlaps_source`, or `scheduler_state_location_not_applicable`). orgId is not a valid org id (`invalid_org_id`), or X-Org-Name does not name that org (`org_path_mismatch`).

**401 Unauthorized**

Unauthorized

**402 Payment Required**

The source runs models on hosted AI credits, which the organization has used up (`code: ai_credits_exhausted`).

**403 Forbidden**

Insufficient scope, a viewer role, or the source runs more replicas or executors than the plan or the dedicated cluster allows. The caller is not a member of the org (`org_forbidden`). A machine credential named a different org (`org_assertion_mismatch`). The org is deleted (`org_deleted`).

**404 Not Found**

Project or organization not found

**409 Conflict**

A project with the requested name already exists, another project creation is in progress, or every default name is taken (`code: fork_name_unavailable`)

**413 Content Too Large**

Request body too large

**422 Unprocessable Content**

The source has no spicepod (`code: fork_source_has_no_spicepod`), its spicepod is not valid (`code: fork_source_spicepod_invalid`), or its spicepod loads files from its GitHub repository besides spicepod.yaml (`code: fork_source_loads_repository_files`)

**429 Too Many Requests**

Too many project creation requests (rate limited)

**500 Internal Server Error**

The fork could not be created. When the failure comes after the project row is created, the fork is deleted again with its spicepod, secrets, and organization secret links. If the project itself cannot be deleted, the response has `code: fork_rollback_failed` and names the partially created project in `project` (`id` and `name`). Delete that project, by `project.id`, before reusing its name.

**502 Bad Gateway**

The source is connected to a GitHub repository, and its spicepod could not be read from it (`code: fork_source_repository_unreadable`)

## `POST /v1/apps/{appId}/forks` (deprecated)

Legacy alias of `/v1/projects/{projectId}/forks` — projects were previously called apps. Creates a new project from this project's configuration and records this project as its source. The fork copies the spicepod the project deploys, its linked connections, project secrets, linked organization secrets, update channel, version range, replica and executor counts, storage size, description, and tags. It does not copy API keys (new ones are generated), deployments, the GitHub repository link, the instance size, or visibility (forks are private). The fork is not deployed: deploy it with POST /v1/projects/{projectId}/deployments. Requires `apps:write`. The server copies secret values without returning them, so no secrets scope is needed. A source connected to a GitHub repository is read from that repository, and its spicepod is copied only when it loads no other file from the repository; to deploy a fork from a fork of the repository, fork the project in the Spice portal.

### Path parameters

- `appId` (integer, required): The ID of the project to fork

### Header parameters

- `Authorization` (string, required): `Bearer <token>`. [Authentication](https://spice.ai/docs/cloud/api/management#authentication) lists the tokens it accepts.
- `X-Org-Name` (string, optional): The organization to act on, by handle. Omit it to act on the organization the credential is bound to; sending it blank is a 400, not the same as omitting it. A user credential may name any organization its owner belongs to; a machine credential is pinned to its own. A portal session token is bound to none, so it must send this header on every route but `GET /v1/orgs` (400 `org_assertion_required` otherwise). Pattern: `^[A-Za-z0-9._-]{1,255}$`.

### Request body

Optional.

Content type: `application/json`

- `name` (string): Name of the fork. Defaults to the source name with `-fork`, then `-fork-2` and so on, whichever is free. Minimum length: `4`. Maximum length: `38`. Pattern: `^[a-zA-Z0-9-]+$`.
- `region` (string): Region to place the fork in. Omit it and `cluster_name` to place the fork where the source runs. Allowed values: `us-east-1`, `us-west-2`.
- `cluster_name` (string): Dedicated cluster to place the fork on, as listed by `GET /v1/clusters`. If `region` is also set, it must match the cluster's region.
- `scheduler_state_location` (string): s3:// URI for the fork's scheduler state. Required when the source spicepod sets `runtime.scheduler.state_location`, and must not overlap it: two schedulers cannot share a location. Maximum length: `2048`.
- `default_alerts` (boolean): Create default monitoring alerts for the new managed fork. False skips initial setup. Manual alert creation remains available. Default: `true`.

### Example request

```bash
curl -X POST "https://api.spice.ai/v1/apps/<appId>/forks" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "string",
    "region": "us-east-1",
    "cluster_name": "string",
    "scheduler_state_location": "string",
    "default_alerts": true
  }'
```

```http
POST /v1/apps/<appId>/forks HTTP/1.1
Host: api.spice.ai
Authorization: Bearer <token>
Content-Type: application/json
Content-Length: 141

{
  "name": "string",
  "region": "us-east-1",
  "cluster_name": "string",
  "scheduler_state_location": "string",
  "default_alerts": true
}
```

### Responses

**201 Created**

Project forked. It has not been deployed yet.

Content type: `application/json`

- `id` (integer): Unique identifier for the project
- `name` (string): Name of the project
- `kind` (string): Resolved project hosting model. Allowed values: `managed`, `standalone`, `byoc`.
- `description` (string | null): Description of the project
- `visibility` (string): Visibility setting for the project. Allowed values: `public`, `private`.
- `created_at` (string (date-time)): Timestamp when the project was created
- `cname` (string | null): Region identifier
- `cluster_name` (string | null): Resolved dedicated cluster name; null for projects not assigned to a dedicated cluster.
- `endpoint` (string | null): The data-plane endpoint the project connects to, or null before a standalone instance is attached.
- `production_branch` (string | null): Production branch for the project
- `api_key` (string, deprecated): Deprecated. Use the API Keys endpoints instead.
- `tags` (map of string | null): Key-value tags for the project
- `forked_from` (object | null): The project this project was forked from, while that project is live in the same organization. Null for a project that was not created as a fork.
  - `id` (integer, required): ID of the source project
  - `name` (string, required): Name of the source project
- `paused_at` (string (date-time) | null): Timestamp when the project was paused; null while running
- `warning` (string | null): Present when the project was created but initial configuration could not be applied
- `config` (object | null): Runtime configuration; null for an unattached standalone project.
  - `spicepod` (object | null): Spicepod configuration
  - `registry` (string | null): Container registry for the runtime image
  - `image_tag` (string | null): Runtime image tag
  - `update_channel` (string | null): Update channel for the runtime. Allowed values: `stable`, `preview`, `nightly`.
  - `replicas` (integer): Number of replicas
  - `resources` (object | null): Resource requests and limits for the project container
    - `limits` (object)
      - `cpu` (string): Whole-number vCPU limit, or '-' for no CPU limit
      - `memory` (string): Memory limit in Gi (for example, 16Gi)
      - `ephemeral-storage` (string)
    - `requests` (object)
      - `cpu` (string): CPU request. Minimum 100 millicores (for example, 100m or 0.1).
      - `memory` (string): Memory request. Minimum 192Mi.
  - `executor` (object | null): Executor container configuration
    - `replicas` (integer): Number of executor replicas
    - `resources` (object): Resource requests and limits for the executor container
      - `limits` (object)
        - `cpu` (string): Whole-number vCPU limit, or '-' for no CPU limit
        - `memory` (string): Memory limit in Gi (for example, 16Gi)
        - `ephemeral-storage` (string)
      - `requests` (object)
        - `cpu` (string): CPU request. Minimum 100 millicores (for example, 100m or 0.1).
        - `memory` (string): Memory request. Minimum 192Mi.
  - `region` (string | null): Cloud region code (e.g., us-east-1)
  - `node_group` (string | null): Node group for scheduling the project runtime
  - `storage_claim_size_gb` (number | null): Storage claim size in GB
- `shared_state` (array of object, required): Settings copied as they are that name external state the source also uses: a replication slot or Kafka consumer group the spicepod names, or a snapshot location. Change them in the fork's spicepod before deploying it if the two projects should not share that state.
  - `component` (string, required): The dataset, or `snapshots`
  - `setting` (string, required): Example: `pg_replication_slot`.
  - `value` (string, required)

**400 Bad Request**

Invalid project ID or request body, an unknown region or cluster, no `region` or `cluster_name` set for a source whose placement a fork cannot reuse (a BYOC cluster, a self-hosted runtime, or a region or cluster that is no longer available; `code: fork_placement_required`), or a missing or unusable `scheduler_state_location` (`code: scheduler_state_location_required`, `scheduler_state_location_unsupported`, `scheduler_state_location_overlaps_source`, or `scheduler_state_location_not_applicable`).

**401 Unauthorized**

Unauthorized

**402 Payment Required**

The source runs models on hosted AI credits, which the organization has used up (`code: ai_credits_exhausted`).

**403 Forbidden**

Insufficient scope, a viewer role, or the source runs more replicas or executors than the plan or the dedicated cluster allows

**404 Not Found**

Project or organization not found

**409 Conflict**

A project with the requested name already exists, another project creation is in progress, or every default name is taken (`code: fork_name_unavailable`)

**413 Content Too Large**

Request body too large

**422 Unprocessable Content**

The source has no spicepod (`code: fork_source_has_no_spicepod`), its spicepod is not valid (`code: fork_source_spicepod_invalid`), or its spicepod loads files from its GitHub repository besides spicepod.yaml (`code: fork_source_loads_repository_files`)

**429 Too Many Requests**

Too many project creation requests (rate limited)

**500 Internal Server Error**

The fork could not be created. When the failure comes after the project row is created, the fork is deleted again with its spicepod, secrets, and organization secret links. If the project itself cannot be deleted, the response has `code: fork_rollback_failed` and names the partially created project in `project` (`id` and `name`). Delete that project, by `project.id`, before reusing its name.

**502 Bad Gateway**

The source is connected to a GitHub repository, and its spicepod could not be read from it (`code: fork_source_repository_unreadable`)

OpenAPI: [api.spice.ai/openapi.json](https://api.spice.ai/openapi.json)
