Virtual machines
Create, boot, stop, start and delete virtual machines, including desktop machines for AI agents, from your own code.
A virtual machine is a service like any other: it lives in an environment, on the project's private network, and is created, read and deleted through the same service endpoints. Stopping and starting are the two calls only VMs have.
A desktop machine is a VM created from the ubuntu-24.04-desktop image. Once it is running, an AI agent drives it through the computer API.
Every call takes a project token (sk_proj_…) as a bearer token. Throughout this page:
BASE = https://api.cloud.simplifyd.com/v1/workspaces/{workspace}/projects/{project}/envs/{env}/svcs{workspace}, {project}, {env} | Ids. See how resources are addressed. |
{service} | The VM's id, the slug returned when you create it. |
For desktop session tokens, see Show the screen in your own app. For screenshots, display details and computer actions, see Computer API.
Endpoints
| Method | Path | What it does |
|---|---|---|
POST | {BASE} | Creates a VM |
POST | {BASE}/{service}/deployments | Boots a new VM |
GET | {BASE}/{service} | Its configuration and status |
POST | {BASE}/{service}/vm/stop | Shuts it down, keeping its disk |
POST | {BASE}/{service}/vm/start | Boots a stopped VM |
DELETE | {BASE}/{service} | Deletes it and its disk |
A VM's life is: create it, deploy it once to boot it, then stop and start it as you need it. Delete it when you are done.
Create a VM
POST {BASE}
Content-Type: application/jsonCreates the VM. It does not boot until you deploy it.
A VM starts with 2 vCPUs and 4 GB of memory. In a workspace that has never been funded it gets 1 vCPU instead.
Body
| Field | Description | |
|---|---|---|
type | Required | "vm". |
name | Optional | Display name. Defaults to VM. |
kubevirt_vm_svc.image | Required | ubuntu-22.04, or ubuntu-24.04-desktop for a desktop machine. |
kubevirt_vm_svc.storage_gb | Required | Boot disk size in GB: 10 to 200, or 20 to 200 for a desktop machine. It cannot be made smaller later. |
kubevirt_vm_svc.ssh_authorized_keys | Required for ubuntu-22.04 | 1 to 10 public keys, installed for the ubuntu user. Optional for a desktop machine, which an agent reaches through its screen and the computer API. |
curl -X POST "$BASE" \
-H "Authorization: Bearer $SIMPLIFYD_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "vm",
"name": "research",
"kubevirt_vm_svc": { "image": "ubuntu-24.04-desktop", "storage_gb": 20 }
}'const response = await fetch(BASE, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIMPLIFYD_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'vm',
name: 'research',
kubevirt_vm_svc: { image: 'ubuntu-24.04-desktop', storage_gb: 20 },
}),
});
const vm = await response.json();client := cloud.NewClient(cloud.WithToken(os.Getenv("SIMPLIFYD_API_TOKEN")))
svcs := client.Workspace(ws).Project(p).Env(env).Services()
vm, err := svcs.Create(ctx, cloud.CreateServiceInput{
Name: "research",
Type: cloud.ServiceTypeVM,
VM: &cloud.VMInput{
Image: cloud.VMImageDesktop,
StorageGB: 20,
},
})Response
201 Created. The new service, trimmed here to the fields that matter:
{
"slug": "01a137c0-b1af-7298-a143-1aa7f2557b0b",
"name": "research",
"type": "vm",
"vcpus": 2,
"memory": 4096,
"replicas": 1,
"region": "ng-west-1"
}slug is the {service} in every other call. memory is in MB.
Errors
| Status | When |
|---|---|
400 | The image is not one of the above, storage_gb is missing or out of range, or a server image has no SSH key. message names the field. |
402 | The workspace is suspended for an unpaid balance. |
Boot it for the first time
POST {BASE}/{service}/deployments?auto_approve_change_sets=trueBoots a VM you have just created. Call it once. After that, use stop and start.
Query parameters
| Parameter | Description | |
|---|---|---|
auto_approve_change_sets | Optional | true applies any pending changes to the service as part of this deploy. Without it, a service with pending changes is refused with 400. |
curl -X POST "$BASE/$SERVICE/deployments?auto_approve_change_sets=true" \
-H "Authorization: Bearer $SIMPLIFYD_API_TOKEN"await fetch(`${BASE}/${service}/deployments?auto_approve_change_sets=true`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SIMPLIFYD_API_TOKEN}` },
});_, err = svcs.Deploy(ctx, vm.Slug, cloud.DeployOptions{AutoApproveChangeSets: true})Response
201 Created
{ "success": true }The VM is booting. Get it to watch status reach running. A desktop machine's screen and computer API are usually ready one to two minutes after the call; until then the computer API answers 503.
Get a VM
GET {BASE}/{service}curl "$BASE/$SERVICE" \
-H "Authorization: Bearer $SIMPLIFYD_API_TOKEN"const response = await fetch(`${BASE}/${service}`, {
headers: { Authorization: `Bearer ${process.env.SIMPLIFYD_API_TOKEN}` },
});
const vm = await response.json();vm, err := svcs.Get(ctx, slug)
if vm.VM.HasDesktop() && vm.Status == cloud.ServiceStatusRunning {
// ready to drive, give or take the desktop signing in
}Response
200 OK. Trimmed to the fields that matter for a VM:
{
"slug": "01a137c0-b1af-7298-a143-1aa7f2557b0b",
"name": "research",
"type": "vm",
"status": "running",
"vcpus": 2,
"memory": 4096,
"region": "ng-west-1",
"private_hostname": "research.p28xl.0000001.simplifyd.internal",
"kubevirt_vm_svc": {
"image": "ubuntu-24.04-desktop",
"storage_gb": 20
}
}| Field | Description |
|---|---|
status | pending before the first deploy; deploying or starting while it boots; running; stopped; failed if it could not start, with status_message saying why. |
private_hostname | Where other services in the project reach it. |
kubevirt_vm_svc | The image, disk size and SSH keys it was created with. |
Stop a VM
POST {BASE}/{service}/vm/stopShuts the VM down. Its disk is kept, with everything on it. Compute stops being billed; the disk is billed while the VM is stopped.
curl -X POST "$BASE/$SERVICE/vm/stop" \
-H "Authorization: Bearer $SIMPLIFYD_API_TOKEN"await fetch(`${BASE}/${service}/vm/stop`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SIMPLIFYD_API_TOKEN}` },
});err = svcs.StopVM(ctx, slug)Response
200 OK
{ "success": true }status is stopped from then on.
Errors
| Status | When |
|---|---|
400 | The service is not a VM: "service does not support stop/start". |
Start a VM
POST {BASE}/{service}/vm/startBoots a stopped VM from the disk it was stopped with.
curl -X POST "$BASE/$SERVICE/vm/start" \
-H "Authorization: Bearer $SIMPLIFYD_API_TOKEN"await fetch(`${BASE}/${service}/vm/start`, {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SIMPLIFYD_API_TOKEN}` },
});err = svcs.StartVM(ctx, slug)Response
200 OK
{ "success": true }status is deploying until the VM is up, then running. As after the first boot, a desktop machine takes one to two minutes to be ready.
Errors
| Status | When |
|---|---|
400 | The service is not a VM, or it has more than 1 vCPU in a workspace that has never been funded. |
Delete a VM
DELETE {BASE}/{service}Deletes the VM and its disk. This cannot be undone.
curl -X DELETE "$BASE/$SERVICE" \
-H "Authorization: Bearer $SIMPLIFYD_API_TOKEN"await fetch(`${BASE}/${service}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${process.env.SIMPLIFYD_API_TOKEN}` },
});err = svcs.Delete(ctx, slug)Response
200 OK, with the deleted service in the same shape as get.
Errors
Errors carry a message written to be shown to a person:
{
"type": "VALIDATION_ERROR",
"message": "kubevirt_vm_svc.storage_gb must be between 20 and 200"
}| Status | Meaning |
|---|---|
400 | The request cannot be done as asked; message says why. |
401 | The token is missing, malformed or deleted. |
403 | The token does not cover this workspace, project or environment. |
5xx | Our side. |