Management APIs

Management and Deployment API documentation for api.spice.ai

The Spice.ai Management API (also known as the control-plane API) provides programmatic access to manage Spice.ai Cloud resources—projects, deployments, secrets, API keys, and organization members.

Base URL

https://api.spice.ai

API Version

All API endpoints are versioned under /v1:

https://api.spice.ai/v1

Projects and apps

What the API and portal now call a project was previously called an app. The resource is unchanged — only the name is different.

Both path prefixes reach the same handlers:

PathStatusList response envelope
/v1/projectsCanonical{ "projects": [...] }
/v1/appsLegacy, still served{ "apps": [...] }

Two details matter when migrating:

  • The list envelope differs. GET /v1/projects returns results under a projects key, while GET /v1/apps keeps its original apps key. A client switching to the canonical path must read the new key. All other response shapes and field names are identical, including the id and name fields on each resource.
  • OAuth scope names are unchanged. The scopes are still apps:read, apps:write, and apps:delete, because they are embedded in already-issued tokens. They grant access to projects under either path.

The Spice CLI, Terraform provider, and SDKs continue to call the legacy paths and are unaffected.

Authentication

The Management API supports three authentication methods:

1. Personal Access Tokens (PATs)

PATs are long-lived, user-scoped tokens. Recommended for:

  • CLI tools and automation scripts
  • Personal integrations

Creating a PAT:

  1. Sign in to Spice.ai Cloud Portal
  2. Navigate to Profile → Personal Access Tokens
  3. Click Create Token
  4. Select an organization and configure scopes
  5. Copy the token (it won't be shown again)

Using a PAT:

curl -H "Authorization: Bearer <your-pat-token>" \
  https://api.spice.ai/v1/projects

Learn more: Personal Access Tokens

2. OAuth 2.0 Client Credentials

OAuth client credentials are organization-scoped tokens. Ideal for:

  • CI/CD pipelines
  • Service-to-service authentication
  • Multi-tenant applications
  • Third-party integrations

Step 1 — Exchange client credentials for an access token:

curl -X POST https://spice.ai/api/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "your-client-id",
    "client_secret": "your-client-secret",
    "grant_type": "client_credentials"
  }'

The response contains an access_token:

{
  "access_token": "eyJhbGciOiJSUzI...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Step 2 — Use the access token in subsequent requests:

curl -H "Authorization: Bearer <access-token>" \
  https://api.spice.ai/v1/projects

3. User Session Tokens (CLI)

The Spice CLI stores a credential for the Management API with spice login. On a terminal it asks which one to use:

spice login
? How would you like to authenticate to Spice Cloud? ›
❯ Login with a web browser
  Paste an access token

Login with a web browser signs in through the browser and stores the resulting user session token, which carries the user's identity. Paste an access token stores a personal access token instead.

The prompt appears only when the CLI is attached to a terminal. Scripts and CI pipelines name the method as a subcommand, which also lets them supply the credential without being prompted for it:

CommandCredential storedSupplying it without a prompt
spice login subscriptionUser session token--device prints the URL and a one-time code to enter on another device instead of opening a browser, for SSH and headless shells
spice login tokenPersonal access token--token, or the SPICE_CLOUD_PAT environment variable
spice login apiOAuth client credentials--client-id and --client-secret, or SPICE_CLOUD_CLIENT_ID and SPICE_CLOUD_CLIENT_SECRET

Run with no terminal and no subcommand, spice login reports that the login type must be chosen explicitly rather than waiting on a prompt that cannot be answered.

Organization context

Every Management API request acts on exactly one organization. By default that is the organization the credential was minted against. Send the X-Org-Name header with an organization handle to act on a different one.

CredentialOrganizations it can act on
Personal access token or CLI sessionAny organization the token owner belongs to
OAuth client credentialsOnly the organization the client was issued to

A user credential carries a user identity, so the API authorizes the request against that user's membership. A machine credential carries no user identity and stays pinned to one organization: a header naming a different organization is refused rather than ignored.

Handles are matched case-insensitively and may contain letters, numbers, dots, hyphens, and underscores.

curl -H "Authorization: Bearer <token>" \
  -H "X-Org-Name: acme-corp" \
  https://api.spice.ai/v1/projects

Discovering organizations

GET /v1/orgs lists the organizations the caller belongs to, with the caller's role in each. Use it to choose a value for X-Org-Name.

curl -H "Authorization: Bearer <token>" \
  https://api.spice.ai/v1/orgs
{
  "orgs": [
    { "id": 42, "name": "acme-corp", "role": "owner" },
    { "id": 57, "name": "example-org", "role": "member" }
  ]
}
  • name is the organization handle — the value X-Org-Name accepts, and the one used in <org>/<project> addressing.
  • role is the caller's highest-privilege role in that organization: owner, admin, member, or viewer.
  • A caller who belongs to no organization receives 200 with an empty orgs array, never a 404.
  • An OAuth client credential lists only the organization it was issued to, with the role owner.
  • Requires the apps:read scope.

Refused requests

A refusal returns a machine-readable code alongside error, because each one has a different remedy:

CodeStatusMeaningRemedy
org_forbidden403The named organization does not exist or is not visible to the callerVerify the handle; if it is correct, ask an organization owner for an invitation
forbidden403The caller is a member but lacks the role the action needsAsk an owner or admin for a higher role
insufficient_scope403The credential was not granted the required scopeReissue the credential with the scope
org_assertion_mismatch403A machine credential named an organization other than its ownUse a credential issued for that organization
invalid_org_assertion400The header value is not a valid organization handleCorrect the handle

Two details are easy to miss:

  • A blank X-Org-Name is an error, not an omission. It returns invalid_org_assertion, because a client that sent the header believes it named an organization.
  • An organization that does not exist and one the caller cannot see both return org_forbidden. The header cannot be used to test whether an organization exists.

OAuth Scopes

Access to API resources is controlled through scopes. PATs and OAuth clients must be granted appropriate scopes:

ScopeDescription
*Full access to all resources (not recommended for production)
apps:readRead project information
apps:writeCreate and update projects
apps:deleteDelete projects
deployments:readView deployment status and history
deployments:writeCreate new deployments
secrets:readList and view secrets (values are masked)
secrets:writeCreate, update, and delete secrets
config:readRead project configuration
config:writeUpdate project configuration
members:readView organization members
members:writeAdd and update organization members
members:deleteRemove organization members

Scope hierarchy:

  • A write scope automatically includes its corresponding read scope (e.g. apps:write implies apps:read).
  • The wildcard scope (*) grants all permissions.
  • The apps:* scope names are unchanged by the projects rename, and apply to projects.

Rate Limiting

Requests are rate-limited per project. These limits are a high-level failsafe; actual throughput depends on the size of your deployed Spice instance or cluster.

Per-Project Request Rate Limits

PlanRequests / second
Community100
Developer1,000
Pro Teams10,000
Enterprise100,000

Concurrent Query Limits

These limits apply to SQL queries executed against your Spice runtime, not to management API calls.

PlanConcurrent QueriesQuery Timeout
Developer1690 seconds
Pro Teams645 minutes
Enterprise1,02430 minutes

Error Responses

The API uses standard HTTP status codes:

Status CodeDescription
200 OKRequest succeeded
201 CreatedResource created successfully
202 AcceptedRequest accepted (async operation)
204 No ContentRequest succeeded with no response body
400 Bad RequestInvalid request body or parameters
401 UnauthorizedMissing or invalid authentication
403 ForbiddenInsufficient scope or permissions
404 Not FoundResource not found
409 ConflictResource already exists or conflict
410 GoneThe token or resource has expired
413 Payload Too LargeRequest body exceeds the 1 MiB limit
422 Unprocessable EntityValid request that cannot be completed as sent
429 Too Many RequestsRate limit exceeded
500 Internal Server ErrorServer error
502 Bad GatewayAn upstream service failed
503 Service UnavailableA required service is temporarily unavailable

Error Response Format:

{
  "error": "Human-readable error message",
  "details": {
    "fieldErrors": {
      "field_name": ["Validation error message"]
    }
  }
}

Authorization failures also carry a machine-readable code, so a client can tell the refusals apart instead of guessing:

{
  "error": "You are not a member of the requested organization",
  "code": "org_forbidden"
}

See Refused requests for the codes and their remedies.

Pagination

List endpoints support cursor-based pagination with the following query parameters:

ParameterTypeDefaultDescription
limitinteger20Maximum number of items to return (max: 100)
offsetinteger0Number of items to skip

OpenAPI Specification

The API publishes its own OpenAPI specification, which describes every endpoint and the headers it accepts:

https://api.spice.ai/openapi.json

The same document is also served at https://api.spice.ai/v1/docs and https://api.spice.ai/v1/docs/openapi.json.

SDK Support

Official SDKs are available for popular languages:

Endpoints

  • Health - API health check
  • Regions - List available deployment regions
  • Projects - Manage Spice projects
  • Deployments - Deploy and manage project deployments
  • Secrets - Manage project secrets
  • API Keys - Manage project API keys
  • Members - Manage organization members
  • Organizations - List the organizations the caller belongs to
  • Metrics - Scrape per-project runtime metrics
  • Container Images - List available runtime versions

Terraform Provider

Manage Spice.ai resources as infrastructure-as-code with the Spice.ai Terraform Provider. See the Terraform Provider page for resources, data sources, import instructions, and complete examples.

Examples

List all projects

curl -H "Authorization: Bearer <token>" \
  https://api.spice.ai/v1/projects

Results are returned under a projects key. The legacy GET /v1/apps path returns the same records under an apps key.

Create a new project

curl -X POST https://api.spice.ai/v1/projects \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my-project",
    "region": "us-west-2",
    "description": "My Spice project",
    "visibility": "private"
  }'

Organizations with a dedicated cluster can pass cluster_name in place of region to create the project on their dedicated infrastructure.

Update a project

curl -X PUT https://api.spice.ai/v1/projects/123 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "replicas": 2
  }'

Create a deployment

curl -X POST https://api.spice.ai/v1/projects/123/deployments \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "branch": "main",
    "commit_sha": "abc123",
    "commit_message": "Deploy latest changes"
  }'

Deployment status and errors

Deployment statuses

Deployments use these statuses:

StatusMeaning
queuedThe deployment is awaiting platform processing.
in_progressThe deployment is active. When Cloud is retrying a scheduling block, error_code and error_message are set together. Ordinary waiting has both fields null.
succeededThe deployment is terminal. For Spice-managed compute, the runtime is ready. For a standalone Cloud Connect target, Cloud accepted the deployment for dispatch. This status does not confirm that the remote runtime is ready.
failedThe deployment is terminal. Current writes include the catalog error_code and error_message.
createdThe deployment is a superseded historical record.

Deployment errors

The error catalog classifies codes as retriable or terminal:

error_codeClassification
insufficient_cpuRetriable
insufficient_memoryRetriable
insufficient_storageRetriable
insufficient_instancesRetriable
pod_exceeds_node_capacityTerminal
unable_to_startTerminal
internal_errorTerminal

error_code is optional. When it is present, it is a catalog code. error_message contains human-readable catalog text. On current writes, the two fields are set together and cleared together. Older failed deployments can carry an error_message without an error_code. Branch on error_code when it is present, not on the message text.

A project instance that cannot start because the cluster lacks CPU, memory, storage, or instance capacity is a retriable scheduling block. GET /v1/projects/{projectId}/deployments and GET /v1/projects/{projectId}/deployments/{deploymentId} keep status as in_progress and return both error_code (insufficient_cpu, insufficient_memory, insufficient_storage, or insufficient_instances) and error_message while Cloud retries placement. That is not a terminal failed deployment. Terminal codes (pod_exceeds_node_capacity, unable_to_start, internal_error) record failed.

The Deployments page in the portal reports the same codes.

Add a secret

curl -X POST https://api.spice.ai/v1/projects/123/secrets \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "DATABASE_URL",
    "value": "postgresql://user:pass@host:5432/db"
  }'

Support

Have questions or running into issues?