Cloud Connect
Reference for the Cloud Connect operations of the Spice Cloud Management API: issue a Cloud Connect enrollment token, enroll a Cloud Connect cluster or standalone runtime, attach an existing project to an enrolled instance, and list projects available for Cloud Connect attachment.
OpenAPIapi.spice.ai/openapi.json
Issue a Cloud Connect enrollment token
POST/v1/cloud-connect/token
Issues a one-time standalone or cluster enrollment token. Requires a user-derived bearer, X-Org-Name, the kind-appropriate write scope, and org admin/owner role. Client-credential tokens are rejected because enrollment admission must retain user audit attribution.
Header parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes |
|
X-Org-Name | string | Yes |
Request body
Required
application/json
One of
kind: standaloneobject
kindstringRequired- Value:
standalone
- Value:
ttl_secondsinteger- Default:
3600 - Minimum:
1 - Maximum:
86400
- Default:
kind: cluster, with nameobject
kindstringRequired- Value:
cluster
- Value:
namestringRequiredregionstringcloud_account_idstringcloud_providerstring- Value:
aws
- Value:
operations_modestring- Allowed values:
managed,approval_required
- Allowed values:
ttl_secondsinteger- Default:
3600 - Minimum:
1 - Maximum:
86400
- Default:
kind: cluster, with connection_idobject
kindstringRequired- Value:
cluster
- Value:
connection_idstringRequiredttl_secondsinteger- Default:
3600 - Minimum:
1 - Maximum:
86400
- Default:
Example request
curl -X POST "https://api.spice.ai/v1/cloud-connect/token" \
-H "Authorization: Bearer <token>" \
-H "X-Org-Name: <X-Org-Name>" \
-H "Content-Type: application/json" \
-d '{
"kind": "standalone"
}'Responses
201Created
Enrollment token issued
application/jsonkindstringRequired- Allowed values:
standalone,cluster
- Allowed values:
tokenstringRequired- Pattern:
^spice-enroll-[A-Za-z0-9_-]{32}$
- Pattern:
expires_atstring (date-time)RequiredorganizationobjectRequired2 properties
idintegerRequirednamestringRequired
targetobject | nullRequiredThe created cluster or rotated connection identifier.
400Bad Request
Invalid request or missing organization assertion
401Unauthorized
Unauthorized
403Forbidden
Insufficient scope, role, or plan
409Conflict
Cluster has already enrolled
413Content Too Large
Request body too large
429Too Many Requests
Token issuance rate limit exceeded
500Internal Server Error
Internal token issuance failure
Enroll a Cloud Connect cluster or standalone runtime
POST/v1/cloud-connect/enroll
Token enrollment accepts a canonical kind-bound enrollment token. Standalone enrollment may instead use user Authorization plus X-Org-Name. Idempotency-Key is required for standalone enrollment.
Header parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization | string | No |
|
Idempotency-Key | string (uuid) | No | |
X-Org-Name | string | No | Required with Bearer authorization for authenticated standalone enrollment; omitted for token enrollment. |
Request body
Required
application/json
One of
kind: standalone, with tokenobject
kindstringRequired- Value:
standalone
- Value:
tokenstringRequired- Pattern:
^spice-enroll-[A-Za-z0-9_-]{32}$
- Pattern:
expected_orgstringcsr_pemstringRequiredenc_pubkey_pemstringRequiredinstanceobjectRequiredregionstring
kind: clusterobject
kindstringRequired- Value:
cluster
- Value:
tokenstringRequired- Pattern:
^spice-enroll-[A-Za-z0-9_-]{32}$
- Pattern:
expected_orgstringcsr_pemstringRequiredenc_pubkey_pemstringRequired
kind: standalone, without tokenobject
kindstringRequired- Value:
standalone
- Value:
csr_pemstringRequiredenc_pubkey_pemstringRequiredinstanceobjectRequiredregionstring
Example request
curl -X POST "https://api.spice.ai/v1/cloud-connect/enroll" \
-H "Content-Type: application/json" \
-d '{
"kind": "standalone",
"token": "string",
"csr_pem": "string",
"enc_pubkey_pem": "string",
"instance": {}
}'Responses
200OK
Enrollment completed or exactly replayed
application/jsonOne of
kind: standaloneobject
kindstringRequired- Value:
standalone
- Value:
instance_idstringRequiredidentity_cert_pemstringRequiredca_bundle_pemstringRequiredgateway_addrstringRequirednot_afterstring (date-time)RequiredorganizationobjectRequired2 properties
idintegerRequirednamestringRequired
regionstringRequiredportalobjectRequired1 property
new_project_urlstring (uri)Required
attachmentnullRequiredrecoveredbooleanRequired
kind: clusterobject
kindstringRequired- Value:
cluster
- Value:
connection_idstringRequiredidentity_cert_pemstringRequiredca_bundle_pemstringRequiredgateway_addrstringRequirednot_afterstring (date-time)RequiredorganizationobjectRequired2 properties
idintegerRequirednamestringRequired
regionstringRequiredattachmentnullRequired
400Bad Request
Invalid request, kind mismatch, or unsupported project field
401Unauthorized
Invalid credential or authorization
403Forbidden
Authenticated caller lacks the required scope or organization role
409Conflict
Consumed token, connection already enrolled, unavailable connection, organization mismatch, or idempotency mismatch
410Gone
Enrollment token or authenticated recovery window expired
413Content Too Large
Request body too large
500Internal Server Error
Internal or identity-provider failure; use the retryable response field
Attach an existing project to an enrolled instance
POST/v1/cloud-connect/attach
Attaches an existing standalone project to an enrolled standalone instance and assigns the project's region and cname from the stamp whose gateway terminates that instance's control stream. The optional location field is reserved for multi-region use and is ignored today.
This operation is dual-authorized. A user-derived bearer plus the X-Org-Name assertion authorizes the project mutation; OAuth client credentials are refused. The caller must have the instances:write and apps:write scopes and the organization admin or owner role. The cert_pem and pop_sig body fields prove only that the request concerns the named instance. Instance proof by itself never authorizes project attachment.
pop_sig is a base64 DER ECDSA (SHA-256) signature over the UTF-8 bytes spice-cloud-connect/attach/v1\n{X-Org-Name}\n{instance_id}\n{project_id}. The project must already have kind standalone and no cluster target. The database is the authority for attachment conflicts; clients must not pre-check or infer an attachment limit from this contract.
Errors use {code, error, retryable}. A 409 always has retryable: false; a 5xx has retryable: true so the client exact replays any request whose attachment may already have committed.
Header parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes |
|
X-Org-Name | string | Yes | Organization that owns both the project and instance; also bound into pop_sig
|
Request body
Required
application/json
instance_idstringRequiredEnrolled instance caller-facing id (
inst_…)- Minimum length:
1 - Maximum length:
200
- Minimum length:
project_idintegerRequiredExisting standalone project id
- Minimum:
1 - Maximum:
9007199254740991
- Minimum:
locationstringReserved for multi-region use; accepted and ignored today
- Minimum length:
1 - Maximum length:
200
- Minimum length:
cert_pemstringRequiredCloud Connect identity certificate for instance_id
- Minimum length:
1 - Maximum length:
8192
- Minimum length:
pop_sigstringRequiredBase64 DER ECDSA signature over the documented attach challenge
- Minimum length:
1 - Maximum length:
512
- Minimum length:
Example request
curl -X POST "https://api.spice.ai/v1/cloud-connect/attach" \
-H "Authorization: Bearer <token>" \
-H "X-Org-Name: <X-Org-Name>" \
-H "Content-Type: application/json" \
-d '{
"instance_id": "string",
"project_id": 1,
"cert_pem": "string",
"pop_sig": "string"
}'Responses
200OK
Project attached, or exact same instance/project replay repaired
application/jsoninstance_idstringRequiredorganizationobjectRequired2 properties
idintegerRequirednamestringRequiredCanonical organization name
projectobjectRequired2 properties
idintegerRequirednamestringRequiredCanonical project name
monitor_urlstring (uri)RequiredCredential-free portal monitoring link for the project
- Pattern:
^https://
- Pattern:
400Bad Request
Invalid body or X-Org-Name (
invalid_requestorinvalid_org_assertion)application/jsoncodestringRequired- Allowed values:
invalid_request,invalid_org_assertion,unauthenticated,forbidden,org_forbidden,org_assertion_mismatch,org_deleted,invalid_instance_proof,instance_not_found,instance_not_enrolled,instance_already_attached,project_not_found,project_not_standalone,project_already_attached,internal
- Allowed values:
errorstringRequiredretryablebooleanRequired
401Unauthorized
Missing/invalid user bearer or invalid instance proof (
unauthenticatedorinvalid_instance_proof)application/jsoncodestringRequired- Allowed values:
invalid_request,invalid_org_assertion,unauthenticated,forbidden,org_forbidden,org_assertion_mismatch,org_deleted,invalid_instance_proof,instance_not_found,instance_not_enrolled,instance_already_attached,project_not_found,project_not_standalone,project_already_attached,internal
- Allowed values:
errorstringRequiredretryablebooleanRequired
403Forbidden
Non-user bearer, insufficient scope, org access, or role refusal (
forbidden,org_forbidden,org_assertion_mismatch, ororg_deleted)application/jsoncodestringRequired- Allowed values:
invalid_request,invalid_org_assertion,unauthenticated,forbidden,org_forbidden,org_assertion_mismatch,org_deleted,invalid_instance_proof,instance_not_found,instance_not_enrolled,instance_already_attached,project_not_found,project_not_standalone,project_already_attached,internal
- Allowed values:
errorstringRequiredretryablebooleanRequired
404Not Found
Instance or project not found in the authorized organization (
instance_not_foundorproject_not_found)application/jsoncodestringRequired- Allowed values:
invalid_request,invalid_org_assertion,unauthenticated,forbidden,org_forbidden,org_assertion_mismatch,org_deleted,invalid_instance_proof,instance_not_found,instance_not_enrolled,instance_already_attached,project_not_found,project_not_standalone,project_already_attached,internal
- Allowed values:
errorstringRequiredretryablebooleanRequired
409Conflict
Instance not enrolled or already attached, project not standalone, or the database reports the project is already attached (
instance_not_enrolled,instance_already_attached,project_not_standalone, orproject_already_attached). These refusals carryretryable: falseand prove this request did not commit an attachment.application/jsoncodestringRequired- Allowed values:
invalid_request,invalid_org_assertion,unauthenticated,forbidden,org_forbidden,org_assertion_mismatch,org_deleted,invalid_instance_proof,instance_not_found,instance_not_enrolled,instance_already_attached,project_not_found,project_not_standalone,project_already_attached,internal
- Allowed values:
errorstringRequiredretryablebooleanRequired
413Content Too Large
Request body exceeds 1 MiB (
invalid_request)application/jsoncodestringRequired- Allowed values:
invalid_request,invalid_org_assertion,unauthenticated,forbidden,org_forbidden,org_assertion_mismatch,org_deleted,invalid_instance_proof,instance_not_found,instance_not_enrolled,instance_already_attached,project_not_found,project_not_standalone,project_already_attached,internal
- Allowed values:
errorstringRequiredretryablebooleanRequired
500Internal Server Error
Internal failure, including post-commit routing repair (
internal, retryable)application/jsoncodestringRequired- Allowed values:
invalid_request,invalid_org_assertion,unauthenticated,forbidden,org_forbidden,org_assertion_mismatch,org_deleted,invalid_instance_proof,instance_not_found,instance_not_enrolled,instance_already_attached,project_not_found,project_not_standalone,project_already_attached,internal
- Allowed values:
errorstringRequiredretryablebooleanRequired
List projects available for Cloud Connect attachment
GET/v1/cloud-connect/attachable-projects
Returns every project the authenticated caller may attach an enrolled Cloud Connect instance to. Policy is evaluated entirely by the server: today, results include only live, unattached standalone projects in the resolved organization, and user credentials require that organization's owner or admin role. Exactly one organization is searched per request and the response names it in org, so an empty projects states which organization has no eligible project rather than leaving a client to wonder whether the one it meant was searched at all; name another with X-Org-Name. Clients must render every returned project and instance without applying their own eligibility filters. The instances array is part of the stable contract and may be populated when server policy permits projects with existing attachments.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | Maximum number of projects to return.
|
cursor | string | No | Opaque cursor returned as |
Header parameters
| Name | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes |
|
X-Org-Name | string | No | Organization to list. A user token may name any organization its owner belongs to; naming one they do not is
|
Example request
curl "https://api.spice.ai/v1/cloud-connect/attachable-projects" \
-H "Authorization: Bearer <token>"Responses
200OK
A page of projects currently eligible for attachment.
application/jsonorgstringRequiredHandle of the organization the page was scoped to, being the one
X-Org-Namenamed or the credential's own when it named none. Always present, so an emptyprojectssays which organization has no eligible project rather than leaving a client to guess whether its active organization was the one searched.projectsarray of objectRequired5 properties
idinteger (int64)RequirednamestringRequiredorgstringRequiredOrganization handle.
regionstring | nullRequiredProject control-plane region, if assigned.
instancesarray of objectRequired3 properties
idstringRequiredInstance external identifier.
locationstring | nullRequiredCustomer-declared instance location.
enrolled_atstring (date-time) | nullRequired
next_cursorstring | nullRequiredOpaque cursor for the next page, or null at the end.
400Bad Request
Invalid organization assertion (
invalid_org_assertion), none from a portal session (org_assertion_required), or invalid limit/cursor (invalid_pagination).401Unauthorized
Missing or invalid bearer token.
403Forbidden
Organization access (
org_forbidden,org_assertion_mismatch, ororg_deleted), apps:read scope (insufficient_scope), or owner/admin role (forbidden) was refused.500Internal Server Error
Organization role, project, or organization-handle lookup failed.