Krova Cloud REST API reference
Create and control Cubes programmatically — the same operations the dashboard and the CLI perform, over HTTP JSON.
The Krova Cloud API manages the full lifecycle of a Cube: create, list, inspect and delete; power off, wake and restart; read SSH connection details and change the SSH port; map custom domains and raw TCP ports; take, list and restore snapshots; download a backup; import an existing machine as a .cube; and register webhook endpoints and read their deliveries. Unauthenticated lookup endpoints cover regions, images and live pricing.
Base URL and version
All endpoints are served under https://krova.cloud/api/v1. The spec is OpenAPI 3.1.0 at version 1.0.0 and is published as machine-readable JSON at /api/v1/openapi.json — point a generator, an agent or an HTTP client straight at it.
Authentication
Send your key in the X-API-KEY header. Keys are scoped to a single Space and inherit the permissions of the membership that created them, so a key can never do more than the member who issued it. There is no OAuth flow and no bearer token.
Rate limits
Mutating requests (POST and DELETE) are limited to 10 requests per 60 seconds per client IP. Read requests are not rate-limited by this rule.
Idempotency
Pass an Idempotency-Key header (up to 255 characters) on a mutating request and a replay returns the original response rather than performing the operation twice, with Idempotency-Replayed: true added to the response headers. Keys are scoped per Space and expire after 24 hours — which makes “create a Cube” safe to retry through a network timeout.
Errors
Errors return a JSON body of the shape { "error": "..." }. The status codes carry the meaning: 400 bad request, 401 missing or invalid API key, 403 authenticated but lacking the required permission, and 404 resource not found.
Custom domains and DNS
Attaching a domain returns the exact DNS records it needs, so an integration never has to hard-code record shapes. An ordinary subdomain needs one CNAME; a wildcard needs three — an ownership TXT, the routing CNAME, and an _acme-challenge CNAME that lets us issue and renew its certificate.
Poll GET /spaces/{spaceId}/cubes/{cubeId}/domains/{mappingId}/records to watch each record go green. Every record reports its own state: missing means it is not published yet — the expected state before you add it, not an error — mismatch means something else is there, and unknown means we could not check, which is never a statement about your DNS. summary.complete turns true only when every record is found.
Two records carry opposite Cloudflare requirements, and both matter. The routing record may be proxied (orange) — that is a supported setup. The _acme-challenge record must be DNS only (grey), because a proxied one answers with Cloudflare's addresses and the certificate authority finds nothing. Each record carries proxyOk and mustBeGrey so you can enforce this automatically.
Quick start
List the Cubes in a Space:
curl https://krova.cloud/api/v1/spaces/$SPACE_ID/cubes \
-H "X-API-KEY: $KROVA_API_KEY"Each Cube comes back in this shape:
{
"id": "cube_...",
"name": "agent-runner",
"state": "running",
"publicIpv4": null,
"image": "ubuntu-24.04",
"sshUser": "ubuntu",
"resources": { "vcpu": 1, "ramGb": 2, "diskGb": 20 }
}state is one of pending, booting, running, stopped, stopping, error or deleted. publicIpv4 is null until you map a port — a Cube has no inbound route from the internet by default. sshUser is the account to log in as — do not infer it from image, which is unchanged by image rebuilds.
Prefer not to call HTTP directly? The same operations are available through the CLI, the TypeScript SDK, and an MCP server for AI agents.
The full endpoint-by-endpoint reference follows.
