Happy-path cookbook
Create (or ensure) a Medium VM, wait until it is usable, then SSH. Pin the OpenAPI spec in CI — field names below are stable on /v1.
Stable fields
Use id, name, plan, status (pending | running | stopped | error), ipAddress, and region. vmId is a deprecated alias of id.
Ready without polling
Subscribe to vm.ready on POST /webhooks (scope webhooks:manage). Verify X-SkyVM-Signature and X-SkyVM-Timestamp. Poll GET /vms/{id} only as a fallback.
Curl
export SKYVM_KEY=sk_live_...
export BASE=https://www.skyvm.cloud/api/v1
# Create a Medium named worker-1 (409 if the name exists)
curl -sS -X POST "$BASE/vms" \
-H "Authorization: Bearer $SKYVM_KEY" \
-H "Content-Type: application/json" \
-d '{"plan":"medium","name":"worker-1","password":"change-me-now"}'
# Prefer ensure for pooled workers — returns the existing box
curl -sS -X PUT "$BASE/vms/worker-1" \
-H "Authorization: Bearer $SKYVM_KEY" \
-H "Content-Type: application/json" \
-d '{"plan":"medium","password":"change-me-now","healthCheck":{"port":8090,"path":"/health"}}'
# Poll until running + public IP (fallback if you skip webhooks)
# Host clone usually finishes within 3 minutes (API timeout 180s).
# Without healthCheck, running means the hypervisor is up.
# With healthCheck, running means GET :8090/health succeeded inside the guest.
ID=42
while true; do
curl -sS "$BASE/vms/$ID" -H "Authorization: Bearer $SKYVM_KEY"
sleep 15
done
ssh root@YOUR_PUBLIC_IPPython
import os, time, requests
BASE = os.environ.get("SKYVM_API_BASE", "https://www.skyvm.cloud/api/v1")
KEY = os.environ["SKYVM_KEY"]
H = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}
r = requests.put(
f"{BASE}/vms/worker-1",
headers=H,
json={
"plan": "medium",
"password": os.environ["SKYVM_VM_PASSWORD"],
"healthCheck": {"port": 8090, "path": "/health"},
},
)
r.raise_for_status()
vm = r.json()
vm_id = vm["id"]
for _ in range(40):
vm = requests.get(f"{BASE}/vms/{vm_id}", headers=H).json()
if vm.get("status") == "running" and vm.get("ipAddress"):
print(vm["id"], vm["name"], vm["ipAddress"])
break
if vm.get("status") == "error":
raise SystemExit(f"VM failed: {vm}")
time.sleep(15)
else:
raise SystemExit("timed out waiting for running + ipAddress")
print(f"ssh root@{vm['ipAddress']}")Auth header is Authorization: Bearer sk_live_.... Missing or invalid keys return 401 unauthorized. A key without vms:write returns 403 forbidden. Duplicate POST /vms names return 409 name_taken. Rotate keys by revoking and creating a new one.