Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Add project forks to cloud. `POST /v1/projects/{projectId}/forks` creates a project from another project's spicepod, connections, secrets, and runtime settings, in the same place or in another region or cluster, and `GET /v1/projects/{projectId}/forks` lists a project's forks. Project responses include `forked_from`. The skill documents the fields, the `shared_state` check before the first deployment of a fork, how a source connected to a GitHub repository is forked, and the error codes. It adds a workflow to copy or move a project to another region, `fork-project` and `list-forks` commands in `scripts/spice-cloud.sh`, and an eval.
- Add the `launch` skill: from a plain-language scenario ("serve Snowflake, Postgres, and S3 data to
agents with OpenAI") to a Spice.ai Cloud project that is designed, deployed, verified, monitored,
and documented. `scripts/spice-launch.sh` creates the managed project (or forks a base project to
Expand Down Expand Up @@ -41,7 +42,6 @@
- Add a "Verify" section to connectors and datasets: `/v1/ready`, then dataset status, then `SELECT … LIMIT 1`.
- AGENTS.md and the README lead with the skill graph from setup; the README's per-agent install details
are collapsed under a summary table. AGENTS.md lists workflow-skill regressions to check for.

- Add the `spice-cookbook` skill, which sets up and runs recipes from the [Spice.ai cookbook](https://github.com/spiceai/cookbook). It picks a recipe by name or goal, fetches the cookbook (or a pull request or branch), and checks the runtime version, Docker, secrets, tools, and ports with `scripts/cookbook.sh` before running the README steps. The script reports whether each secret is set without printing its value.
- Fix spice-setup and spicepod-config, which set bind addresses with `spice run -- --http ...`. That fails on every v2 CLI (`argument '--http' cannot be used multiple times`) because `spice run` already passes `--http`. Use `spice run --http-endpoint`, `--flight-endpoint`, and `--metrics-endpoint` instead.
- Fix spice-search and spice-text-to-sql `rrf()` examples: the score column has been `_fused_score` since v2.0.0, not `fused_score`.
Expand Down
67 changes: 33 additions & 34 deletions skills/cloud/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
name: cloud
description: Manage Spice.ai Cloud resources through spice cloud and the Management API — projects, deployments, monitors, data reactions, secrets, API keys, and organization members. Use for Cloud project lifecycle, deployments, organization context, regions, and infrastructure automation. Use sql, search, chat, or sdk for runtime data and inference APIs, and terraform for infrastructure as code.
description: Manage Spice.ai Cloud resources through spice cloud and the Management API — projects, project forks, deployments, monitors, data reactions, secrets, API keys, and organization members. Use for Cloud project lifecycle (including fork, copy, or move to another region or cluster), deployments, organization context, regions, and infrastructure automation. Use sql, search, chat, or sdk for runtime data and inference APIs, and terraform for infrastructure as code.
---

# Spice.ai Cloud Management

Manage Spice.ai Cloud resources through the Management API (control plane) at `https://api.spice.ai`. Create projects, trigger deployments, configure monitors and data reactions, manage secrets and API keys, and administer organization members.
Manage Spice.ai Cloud resources through the Management API (control plane) at `https://api.spice.ai`. Create and fork projects, trigger deployments, configure monitors and data reactions, manage secrets and API keys, and administer organization members.

**Renamed in Aug 2026:** apps are now **projects**. The `/v1/projects` routes are canonical; every `/v1/apps` route is still served as a legacy alias (marked deprecated in the OpenAPI spec). Only the list envelope differs — `GET /v1/projects` returns `{"projects": [...]}`, `GET /v1/apps` returns `{"apps": [...]}`. Project-management scopes stay `apps:*`; monitors and reactions have separate scopes below.

Expand Down Expand Up @@ -82,13 +82,15 @@ curl -H "Authorization: Bearer $SPICE_API_TOKEN" \

Manage Spice.ai Cloud projects.

| Operation | Method | Path | Scope |
| -------------- | -------- | -------------------------- | ------------- |
| List projects | `GET` | `/v1/projects` | `apps:read` |
| Create project | `POST` | `/v1/projects` | `apps:write` |
| Get project | `GET` | `/v1/projects/{projectId}` | `apps:read` |
| Update project | `PUT` | `/v1/projects/{projectId}` | `apps:write` |
| Delete project | `DELETE` | `/v1/projects/{projectId}` | `apps:delete` |
| Operation | Method | Path | Scope |
| -------------- | -------- | -------------------------------- | ------------- |
| List projects | `GET` | `/v1/projects` | `apps:read` |
| Create project | `POST` | `/v1/projects` | `apps:write` |
| Get project | `GET` | `/v1/projects/{projectId}` | `apps:read` |
| Update project | `PUT` | `/v1/projects/{projectId}` | `apps:write` |
| Delete project | `DELETE` | `/v1/projects/{projectId}` | `apps:delete` |
| Fork project | `POST` | `/v1/projects/{projectId}/forks` | `apps:write` |
| List forks | `GET` | `/v1/projects/{projectId}/forks` | `apps:read` |

### Create Project

Expand Down Expand Up @@ -146,6 +148,19 @@ curl -X DELETE https://api.spice.ai/v1/projects/{projectId} \

Deletes the project and tears down its runtime resources. Returns `204`.

### Fork Project (Sep 2026)

Create a project from another's config via `POST /v1/projects/{projectId}/forks` (`apps:write`); list with `GET .../forks`. Optional body fields: `name`, `region`, `cluster_name`, `scheduler_state_location` (required for a distributed source). The fork is **not deployed**; check `shared_state` before `POST /v1/projects/{forkId}/deployments`. Project responses include `forked_from`. Portal: **Create Project → Fork a project** or **Settings → Forks**.

```bash
curl -X POST https://api.spice.ai/v1/projects/123/forks \
-H "Authorization: Bearer $SPICE_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "analytics-west", "region": "us-west-2"}'
```

Details (what is copied, GitHub-connected sources, status codes): [references/project-forks.md](references/project-forks.md).

## Deployments

Deploy the project's current spicepod.
Expand Down Expand Up @@ -407,6 +422,10 @@ curl -H "Authorization: Bearer $SPICE_API_TOKEN" \
"https://api.spice.ai/v1/projects/123/deployments?limit=1"
```

### Copy or Move a Project to Another Region

Fork into the target region, fix any `shared_state`, deploy the fork, then point clients at the fork's `endpoint` and API keys. To finish a **move**, delete the source only after the user confirms. Full steps: [references/project-forks.md](references/project-forks.md#copy-or-move-a-project-to-another-region).

### Rotate API Keys (Zero Downtime)

```bash
Expand All @@ -433,37 +452,20 @@ curl -X POST https://api.spice.ai/v1/projects/123/api-keys \
A helper script is bundled at `scripts/spice-cloud.sh` (relative to this skill directory) for common operations. It calls the canonical `/v1/projects` routes; the older `*-app` command names still work as aliases.

```bash
# Set your token (personal access token or OAuth access token)
export SPICE_API_TOKEN="your-token"

# List projects (JSON under "projects")
bash scripts/spice-cloud.sh list-projects

# Create project
bash scripts/spice-cloud.sh create-project my-project us-west-2

# Deploy project
bash scripts/spice-cloud.sh fork-project 123 analytics-west us-west-2 # name/region optional
bash scripts/spice-cloud.sh list-forks 123
bash scripts/spice-cloud.sh deploy 123

# Add secret
bash scripts/spice-cloud.sh add-secret 123 DB_PASSWORD secret123

# List deployments
bash scripts/spice-cloud.sh list-deployments 123

# Get API keys
bash scripts/spice-cloud.sh get-api-keys 123
```

## Present Results to User

When presenting management API results:
- Show project IDs and names in a table for list operations
- Show deployment status clearly (queued/in_progress/succeeded/failed), with `error_code` and `error_message` for failures
- Show monitor and reaction IDs, names, statuses, and template IDs; do not print notification targets unless needed
- Never display secret values — confirm creation/update only
- Show API keys with a warning about secure storage
- Give the project's data-plane `endpoint` for queries, and link the portal as `https://spice.ai/<org>/<project>`
When presenting management API results: show project IDs/names in a table; for a fork, name the source, that it is not deployed yet, and any `shared_state`; show deployment status (`queued`/`in_progress`/`succeeded`/`failed`) with `error_code`/`error_message` on failure; show monitor/reaction IDs, names, statuses, and template IDs (omit notification targets unless needed); never display secret values; warn on API keys; give the data-plane `endpoint` and portal link `https://spice.ai/<org>/<project>`.

## Troubleshooting

Expand All @@ -475,6 +477,7 @@ When presenting management API results:
| `403 image_tag_requires_enterprise` | Omit `image_tag`; select the runtime with `update_channel` and `version` instead |
| `404 Project not found` | Verify `projectId` with `GET /v1/projects` in the right organization |
| `409 Conflict` on create project | Project name already exists (names compare case-insensitively) |
| Fork errors (`fork_*`, `scheduler_state_location_*`) | See [references/project-forks.md](references/project-forks.md#fork-error-codes) |
| `409` on deployment | A deployment is already in progress; wait for it to complete |
| `400` on deployment | Project has no spicepod, is paused (`POST .../resume`), or `image_tag` isn't a published version for the channel |
| `400` on create secret | Secret name must start with letter/underscore; letters, numbers, underscores only |
Expand All @@ -483,8 +486,4 @@ When presenting management API results:

## Documentation

- [Management API Reference](https://docs.spice.ai/api/management-api/management)
- [OpenAPI Specification](https://api.spice.ai/openapi.json)
- [Runtime API Reference](https://docs.spice.ai/api)
- [Spice.ai Cloud Changelog](https://docs.spice.ai/changelog)
- [Spice.ai Cloud](https://spice.ai)
[Management API](https://docs.spice.ai/api/management-api/management) · [OpenAPI](https://api.spice.ai/openapi.json) · [Runtime API](https://docs.spice.ai/api) · [Changelog](https://docs.spice.ai/changelog) · [spice.ai](https://spice.ai)
32 changes: 32 additions & 0 deletions skills/cloud/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,38 @@
"description": "Should emphasize zero-downtime approach"
}
]
},
{
"id": 3,
"prompt": "My Spice.ai Cloud project 123 runs in us-east-1. Make a copy of it in us-west-2 called analytics-west, with the same datasets, connections, and secrets, and get it running.",
"expected_output": "Fork the project with POST /v1/projects/123/forks and {\"name\": \"analytics-west\", \"region\": \"us-west-2\"} instead of recreating it by hand, check shared_state in the response, then deploy the fork with POST /v1/projects/{forkId}/deployments because a fork is not deployed on creation",
"files": [],
"assertions": [
{
"name": "forks_project",
"type": "regex",
"value": "(/v1/(projects|apps)/[^/\\s]+/forks|fork-project)",
"description": "Should fork the source project rather than recreate it"
},
{
"name": "targets_region",
"type": "regex",
"value": "\"region\"\\s*:\\s*\"us-west-2\"|fork-project \\S+ \\S+ us-west-2",
"description": "Should place the fork in us-west-2"
},
{
"name": "deploys_fork",
"type": "regex",
"value": "(?i)(/deployments|\\bdeploy\\b)",
"description": "Should deploy the fork, which forking does not do"
},
{
"name": "checks_shared_state",
"type": "regex",
"value": "shared_state",
"description": "Should check the settings the fork shares with its source before deploying"
}
]
}
]
}
68 changes: 68 additions & 0 deletions skills/cloud/references/project-forks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Project forks

Detailed reference for Cloud project forks. The `cloud` skill links here from its Projects and Common Workflows sections.

### Fork Project (Sep 2026)

Create a new project from an existing project's configuration, in the same place or in another region or dedicated cluster. The fork keeps a link to its source: `GET /v1/projects/{projectId}` returns it as `forked_from` (`{"id", "name"}`, or `null` for a project that is not a fork), and `GET /v1/projects/{projectId}/forks` lists a project's forks. In the portal, fork from **Create Project → Fork a project** or the project's **Settings → Forks**.

```bash
curl -X POST https://api.spice.ai/v1/projects/123/forks \
-H "Authorization: Bearer $SPICE_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "analytics-west", "region": "us-west-2"}'
```

| Field | Type | Required | Notes |
| -------------------------- | ------ | ---------------------- | ---------------------------------------------------------------------------- |
| `name` | string | No | Defaults to `<source>-fork`, then `<source>-fork-2`, and so on |
| `region` | string | No | `us-east-1` or `us-west-2`; omit it and `cluster_name` to fork in place |
| `cluster_name` | string | No | Dedicated cluster from `GET /v1/clusters` |
| `scheduler_state_location` | string | For a distributed source | `s3://` URI that must not overlap the source's `runtime.scheduler.state_location` |

The body is optional, and any other field is rejected with `400`.

- **Copied:** the spicepod the source deploys (from its GitHub repository, the internal registry, or the stored config), linked connections, project secrets, linked organization secrets, update channel, version range, replica and executor counts, storage size, description, and tags. The platform-managed Postgres CDC replication slot is rebound to the fork.
- **Not copied:** API keys (the fork gets its own), deployments, the GitHub repository link, the instance size (the fork starts on the default instance), and visibility (forks are private).
- **GitHub-connected sources:** the spicepod is read from the source's repository, and a fork that cannot read it fails with `502 fork_source_repository_unreadable` instead of copying an older stored spicepod. The API copies only `spicepod.yaml`. It refuses a source that loads other files from the repository — component `ref`s, view `sql_ref`s, model or embedding `files`, or relative `file:` sources — with `422 fork_source_loads_repository_files`. To keep a fork in Git, fork the repository on GitHub, name the fork after the new project, then fork the project in the portal with **Deploy from a fork of the repository**. The portal connects the new project to that repository.
- **Not deployed:** the `201` response is the new project with its config. Deploy it with `POST /v1/projects/{forkId}/deployments`.
- **Check `shared_state` first.** The response lists settings copied as-is that name state the source also uses — a replication slot or Kafka consumer group the spicepod names, or a snapshot location. Two projects on one replication slot or consumer group split the changes between them, so change these in the fork's spicepod (`PUT /v1/projects/{forkId}`) before deploying unless the projects should share them.
- A source on a BYOC cluster or a self-hosted (Cloud Connect) runtime has no placement a fork can share, and neither does one whose region or cluster is no longer available: set `region` or `cluster_name`.

**Status codes:** `201` forked (not deployed), `400` validation error (codes in Troubleshooting), `402 ai_credits_exhausted` (the source runs on hosted AI credits the organization has used up), `404` source not found, `409` name taken or `fork_name_unavailable`, `422 fork_source_has_no_spicepod`, `fork_source_spicepod_invalid`, or `fork_source_loads_repository_files`, `429` rate limited, `502 fork_source_repository_unreadable`

### Copy or Move a Project to Another Region

```bash
# 1. Fork project 123 into us-west-2. Note the fork's "id" and read "shared_state".
curl -X POST https://api.spice.ai/v1/projects/123/forks \
-H "Authorization: Bearer $SPICE_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "analytics-west", "region": "us-west-2"}'

# 2. If shared_state lists anything, update the fork's spicepod first (PUT /v1/projects/456).

# 3. Deploy the fork (id 456 here)
curl -X POST https://api.spice.ai/v1/projects/456/deployments \
-H "Authorization: Bearer $SPICE_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"commit_message": "Initial deployment of the fork"}'

# 4. Wait for the deployment to succeed
curl -H "Authorization: Bearer $SPICE_API_TOKEN" \
"https://api.spice.ai/v1/projects/456/deployments?limit=1"
```

Clients then use the fork's `endpoint` and API keys (`GET /v1/projects/456/api-keys`); the source's keys do not work on the fork. To finish a move, delete the source with `DELETE /v1/projects/123` — only after the user confirms, because deleting tears down the source's runtime.

## Fork error codes

| Issue | Solution |
| --- | --- |
| `400 fork_placement_required` | The source runs on a BYOC cluster, a self-hosted runtime, or a region or cluster that is gone; set `region` or `cluster_name` for the fork |
| `400 scheduler_state_location_*` | The source is distributed; set `scheduler_state_location` to an `s3://` URI outside the source's (`_required`, `_unsupported`, `_overlaps_source`). `_not_applicable` means the source is not distributed, so omit the field |
| `409 fork_name_unavailable` | Every default `<source>-fork-N` name is taken; set `name` |
| `422 fork_source_has_no_spicepod` / `fork_source_spicepod_invalid` | The source has no spicepod or an invalid one; fix the source's spicepod, then fork again |
| `422 fork_source_loads_repository_files` | The source's spicepod loads other files from its GitHub repository; fork the repository on GitHub, then fork the project in the portal, which deploys from the forked repository |
| `502 fork_source_repository_unreadable` | The source's spicepod could not be read from its GitHub repository; retry, or fix the repository or the GitHub App's access to it |

Loading
Loading