# Machines API

> Create, list, read, change, start, stop, restart, and delete machines. Every lifecycle change is an asynchronous operation.

- [List machines](#list-machines): `GET /v1/machines`
- [Create a machine](#create-a-machine): `POST /v1/machines`
- [Get a machine](#get-a-machine): `GET /v1/machines/{machine_id}`
- [Change machine settings](#change-machine-settings): `PATCH /v1/machines/{machine_id}`
- [Start a machine](#start-a-machine): `POST /v1/machines/{machine_id}/start`
- [Stop a machine](#stop-a-machine): `POST /v1/machines/{machine_id}/stop`
- [Restart a machine](#restart-a-machine): `POST /v1/machines/{machine_id}/restart`
- [Delete a machine](#delete-a-machine): `POST /v1/machines/{machine_id}/delete`
- [Change a machine's egress](#change-a-machines-egress): `PUT /v1/machines/{machine_id}/network`

## List machines

`GET /v1/machines`

- **API key:** `Authorization: Bearer vmy_…` with preset `read_only`, `use_existing`, `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie.
- **Who:** Any member.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | No | Page size, 1-100 (default 50). |
| `cursor` | query | `string` | No | `next_cursor` from the previous page, with the same `sort` and `order`. |
| `q` | query | `string` | No | Case-insensitive search, at most 128 characters: contained in the display name or tenant alias, a prefix of the machine ID or port label, or a port host (`<label>-<port>...`). Blank means no search. |
| `state` | query | `string` | No | Observed state: `absent`, `provisioning`, `running`, `stopped`, `degraded`, or `unknown`. |
| `desired_state` | query | `string` | No | Desired state: `running`, `stopped`, or `deleted`. |
| `sort` | query | `string` | No | `created_at` (default), `name` (display name), or `state` (observed state). |
| `order` | query | `string` | No | `asc` or `desc`. Defaults to `desc` for `created_at` (newest first) and `asc` otherwise. Ties break on the machine ID in the same direction. |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | — | [`MachinePage`](/docs/api/schemas#machinepage) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | An unknown `sort`, `order`, `state`, or `desired_state`, an oversized `q`, or a malformed cursor or one made for another sort | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl "$VMYARD_API_URL/v1/machines" \
  -H "Authorization: Bearer $VMYARD_API_KEY"
```

```json title="Response 200"
{
  "items": [
    {
      "auto_stop_after_seconds": 0,
      "auto_stop_at": "2026-10-12T06:51:01Z",
      "backend": "mock",
      "created_at": "2026-10-12T05:51:01Z",
      "desired_state": "running",
      "environment": {
        "applied_version_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "error_code": "string",
        "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "status": "pending",
        "version": 0,
        "version_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f"
      },
      "forked_from_machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "idle_since": "2026-10-12T05:36:01Z",
      "idle_sleep_after_seconds": 900,
      "idle_sleep_at": "2026-10-12T05:51:01Z",
      "image": {
        "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "version": 0
      },
      "last_active_at": "2026-10-12T05:36:01Z",
      "latest_snapshot": {
        "completed_at": "2026-10-12T05:51:01Z",
        "created_at": "2026-10-12T05:51:01Z",
        "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "source": "api",
        "status": "pending",
        "tier": "warm"
      },
      "metrics": {
        "cpu_limit_millis": 0,
        "cpu_millis": 0,
        "cpu_percent": 0.5,
        "disk_percent": 0.5,
        "disk_size_bytes": 0,
        "disk_used_bytes": 0,
        "flags": [
          "disk_almost_full"
        ],
        "io_read_bps": 0,
        "io_write_bps": 0,
        "memory_bytes": 0,
        "memory_limit_bytes": 0,
        "memory_percent": 0.5,
        "net_rx_bps": 0,
        "net_tx_bps": 0,
        "sampled_at": "2026-10-12T05:51:01Z",
        "stale": false,
        "stale_reason": "host_unreachable",
        "state": "string"
      },
      "name": "dev",
      "network": {
        "egress": "none",
        "ipv6_address": "string"
      },
      "observed_state": {
        "boot_id": "string",
        "state": "running"
      },
      "organization_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "plan": {
        "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "version": 0
      },
      "region": {
        "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "version": 0
      },
      "resources": {
        "cpu": 0,
        "disk_gib": 0,
        "memory_mib": 0
      },
      "restored_from_snapshot_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "setup_operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
      "ssh": {
        "host": "ssh.eu.example.com",
        "port": 0,
        "user": "vmyard"
      },
      "starting_soon": {
        "expires_at": "2026-10-12T05:51:01Z",
        "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "position": 0,
        "priority": "paid",
        "queued_at": "2026-10-12T05:51:01Z",
        "reason": "no_host_in_region"
      },
      "tenant_alias": "dev"
    }
  ],
  "next_cursor": "string"
}
```

## Create a machine

`POST /v1/machines`

Billing admission can refuse it: `402 billing_required` or `spend_cap_reached`, `403 trial_machine_class_not_allowed`, `409 plan_not_priced`, or `429 limit_reached`. See [Usage, billing, and limits](/docs/guides/billing#limits). Idle sleep is off unless the body sets `idle_sleep_after_seconds` (300 to 86,400); see [Idle sleep](/docs/concepts/idle-sleep). `network: {egress}` (`none` or `internet`) chooses the machine's egress; omitted, the host's default applies, and it can't be combined with `template` (`422`); see [Internet access and IPv6](/docs/guides/networking).

- **API key:** `Authorization: Bearer vmy_…` with preset `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner, Admin, or Operator.
- **Idempotency:** send a unique `Idempotency-Key`; replaying the same key and body returns the same operation.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `X-Organization-ID` | header | `string (uuid)` | No | Organization context. Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`CreateMachineRequest`](/docs/api/schemas#createmachinerequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `auto_stop_after_seconds` | `integer \| null` | No | Seconds the machine may run after it becomes running before the control plane stops it, 60 to 2592000. Omitted means 3600; `null` means never. 60–2592000. |
| `backend` | [`null \| BackendDto`](/docs/api/schemas#backenddto) | No |  |
| `environment` | `string \| null` | No | Environment to apply, by name (alternative to `environment_id`). |
| `environment_id` | `string (uuid) \| null` | No | Environment to apply once the machine is first ready, by ID. Omitting `environment_id`, `environment`, and `no_env` selects the organization's default environment, if it has one. |
| `idle_sleep_after_seconds` | `integer \| null` | No | Seconds the running machine may stay idle (no operation, terminal, or resource activity) before the control plane puts it to sleep, 300 to 86400. Omitted or `null` leaves idle sleep off. 300–86400. |
| `image` | [`null \| VersionedSelectionDto`](/docs/api/schemas#versionedselectiondto) | No |  |
| `name` | `string \| null` | No | Display name, following the `tenant_alias` rules. Defaults to the tenant alias. Unlike the alias it can be changed later. |
| `network` | [`null \| NetworkRequest`](/docs/api/schemas#networkrequest) | No | The machine's egress policy. Omitted, the host's default applies (internet egress unless the operator configured otherwise). Not with `template`: a restored machine keeps its host's policy. Change it later with `PUT /v1/machines/{machine_id}/network`. |
| `never_auto_stop` | `boolean` | No | `true` disables auto-stop. It cannot be combined with `auto_stop_after_seconds`. |
| `no_env` | `boolean` | No | `true` creates the machine without any environment, not even the default (for example, for a machine shared with third parties). |
| `plan` | [`null \| VersionedSelectionDto`](/docs/api/schemas#versionedselectiondto) | No | Required without `template`; with one, the template's exact plan, image, region, resources, and backend are used and these may be omitted (if sent, they must match). |
| `region` | [`null \| VersionedSelectionDto`](/docs/api/schemas#versionedselectiondto) | No |  |
| `resources` | [`null \| ResourcesDto`](/docs/api/schemas#resourcesdto) | No |  |
| `setup_script` | `string \| null` | No | Shell script run once as `/bin/sh -c <script>` after the machine is first ready, at most 65527 bytes without NUL. It is its own exec operation (see `setup_operation_id`) with the runtime's maximum timeout; its failure does not stop the machine. |
| `template` | `string \| null` | No | Create the machine from a ready template (name or ID): a warm restore with its own identity, woken at once. The chosen environment (or the default, or `no_env`) is applied after the restore. |
| `tenant_alias` | `string \| null` | No | Required without `template`. With one, it is the template's (the runtime scopes stored snapshots to their tenant) and may be omitted. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `202` | Accepted. Follow `operation_url` until the operation is terminal. | [`OperationAccepted`](/docs/api/schemas#operationaccepted) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `409` | Conflicts with the current state, such as a reused idempotency key with another body (`idempotency_conflict`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `413` | The request body is too large (`body_too_large`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X POST "$VMYARD_API_URL/v1/machines" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "tenant_alias": "dev",
  "name": "dev",
  "plan": {
    "id": "0b6f6c55-2f1d-4f0e-9a51-6f0f4cba7a10",
    "version": 1
  },
  "image": {
    "id": "4d2b9c1e-7a3f-4e58-b1c6-2f9d8e7a6b50",
    "version": 3
  },
  "region": {
    "id": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
    "version": 1
  },
  "resources": {
    "cpu": 2,
    "memory_mib": 4096,
    "disk_gib": 12
  },
  "backend": "kvm",
  "auto_stop_after_seconds": 7200,
  "setup_script": "apt-get update && apt-get install -y jq"
}'
```

```json title="Response 202"
{
  "machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_url": "/v1/operations/6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "requested_action": "create",
  "status": "pending"
}
```

## Get a machine

`GET /v1/machines/{machine_id}`

- **API key:** `Authorization: Bearer vmy_…` with preset `read_only`, `use_existing`, `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie.
- **Who:** Any member.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `machine_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | — | [`MachineDto`](/docs/api/schemas#machinedto) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl "$VMYARD_API_URL/v1/machines/$MACHINE_ID" \
  -H "Authorization: Bearer $VMYARD_API_KEY"
```

```json title="Response 200"
{
  "auto_stop_after_seconds": 0,
  "auto_stop_at": "2026-10-12T06:51:01Z",
  "backend": "mock",
  "created_at": "2026-10-12T05:51:01Z",
  "desired_state": "running",
  "environment": {
    "applied_version_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "error_code": "string",
    "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "status": "pending",
    "version": 0,
    "version_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f"
  },
  "forked_from_machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "idle_since": "2026-10-12T05:36:01Z",
  "idle_sleep_after_seconds": 900,
  "idle_sleep_at": "2026-10-12T05:51:01Z",
  "image": {
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "version": 0
  },
  "last_active_at": "2026-10-12T05:36:01Z",
  "latest_snapshot": {
    "completed_at": "2026-10-12T05:51:01Z",
    "created_at": "2026-10-12T05:51:01Z",
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "source": "api",
    "status": "pending",
    "tier": "warm"
  },
  "metrics": {
    "cpu_limit_millis": 0,
    "cpu_millis": 0,
    "cpu_percent": 0.5,
    "disk_percent": 0.5,
    "disk_size_bytes": 0,
    "disk_used_bytes": 0,
    "flags": [
      "disk_almost_full"
    ],
    "io_read_bps": 0,
    "io_write_bps": 0,
    "memory_bytes": 0,
    "memory_limit_bytes": 0,
    "memory_percent": 0.5,
    "net_rx_bps": 0,
    "net_tx_bps": 0,
    "sampled_at": "2026-10-12T05:51:01Z",
    "stale": false,
    "stale_reason": "host_unreachable",
    "state": "string"
  },
  "name": "dev",
  "network": {
    "egress": "none",
    "ipv6_address": "string"
  },
  "observed_state": {
    "boot_id": "string",
    "state": "running"
  },
  "organization_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "plan": {
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "version": 0
  },
  "region": {
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "version": 0
  },
  "resources": {
    "cpu": 0,
    "disk_gib": 0,
    "memory_mib": 0
  },
  "restored_from_snapshot_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "setup_operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "ssh": {
    "host": "ssh.eu.example.com",
    "port": 0,
    "user": "vmyard"
  },
  "starting_soon": {
    "expires_at": "2026-10-12T05:51:01Z",
    "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "position": 0,
    "priority": "paid",
    "queued_at": "2026-10-12T05:51:01Z",
    "reason": "no_host_in_region"
  },
  "tenant_alias": "dev"
}
```

## Change machine settings

`PATCH /v1/machines/{machine_id}`

Send only what changes. `idle_sleep_after_seconds` takes 300 to 86,400 seconds, or `null` to turn idle sleep off, and restarts the idle window; out of range answers `422` with a field error. See [Auto-stop](/docs/concepts/auto-stop) and [Idle sleep](/docs/concepts/idle-sleep).

- **API key:** `Authorization: Bearer vmy_…` with preset `use_existing`, `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner, Admin, or Operator.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `machine_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`UpdateMachineRequest`](/docs/api/schemas#updatemachinerequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `auto_stop_after_seconds` | `integer \| null` | No | New auto-stop duration, 60 to 2592000 seconds, or `null` for never. The deadline of a running machine restarts from now. 60–2592000. |
| `idle_sleep_after_seconds` | `integer \| null` | No | New idle-sleep window, 300 to 86400 seconds, or `null` to turn idle sleep off. The idle window restarts. 300–86400. |
| `name` | `string \| null` | No | New display name, following the `tenant_alias` rules. The tenant alias itself is immutable. |
| `never_auto_stop` | `boolean` | No | `true` disables auto-stop. It cannot be combined with `auto_stop_after_seconds`. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | — | [`MachineDto`](/docs/api/schemas#machinedto) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `409` | `machine_deleted`: the machine was deleted | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `413` | The request body is too large (`body_too_large`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X PATCH "$VMYARD_API_URL/v1/machines/$MACHINE_ID" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "api",
  "auto_stop_after_seconds": 3600,
  "idle_sleep_after_seconds": 900
}'
```

```json title="Response 200"
{
  "auto_stop_after_seconds": 0,
  "auto_stop_at": "2026-10-12T06:51:01Z",
  "backend": "mock",
  "created_at": "2026-10-12T05:51:01Z",
  "desired_state": "running",
  "environment": {
    "applied_version_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "environment_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "error_code": "string",
    "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "status": "pending",
    "version": 0,
    "version_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f"
  },
  "forked_from_machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "idle_since": "2026-10-12T05:36:01Z",
  "idle_sleep_after_seconds": 900,
  "idle_sleep_at": "2026-10-12T05:51:01Z",
  "image": {
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "version": 0
  },
  "last_active_at": "2026-10-12T05:36:01Z",
  "latest_snapshot": {
    "completed_at": "2026-10-12T05:51:01Z",
    "created_at": "2026-10-12T05:51:01Z",
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "source": "api",
    "status": "pending",
    "tier": "warm"
  },
  "metrics": {
    "cpu_limit_millis": 0,
    "cpu_millis": 0,
    "cpu_percent": 0.5,
    "disk_percent": 0.5,
    "disk_size_bytes": 0,
    "disk_used_bytes": 0,
    "flags": [
      "disk_almost_full"
    ],
    "io_read_bps": 0,
    "io_write_bps": 0,
    "memory_bytes": 0,
    "memory_limit_bytes": 0,
    "memory_percent": 0.5,
    "net_rx_bps": 0,
    "net_tx_bps": 0,
    "sampled_at": "2026-10-12T05:51:01Z",
    "stale": false,
    "stale_reason": "host_unreachable",
    "state": "string"
  },
  "name": "dev",
  "network": {
    "egress": "none",
    "ipv6_address": "string"
  },
  "observed_state": {
    "boot_id": "string",
    "state": "running"
  },
  "organization_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "plan": {
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "version": 0
  },
  "region": {
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "version": 0
  },
  "resources": {
    "cpu": 0,
    "disk_gib": 0,
    "memory_mib": 0
  },
  "restored_from_snapshot_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "setup_operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "ssh": {
    "host": "ssh.eu.example.com",
    "port": 0,
    "user": "vmyard"
  },
  "starting_soon": {
    "expires_at": "2026-10-12T05:51:01Z",
    "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "position": 0,
    "priority": "paid",
    "queued_at": "2026-10-12T05:51:01Z",
    "reason": "no_host_in_region"
  },
  "tenant_alias": "dev"
}
```

## Start a machine

`POST /v1/machines/{machine_id}/start`

Billing admission can refuse it: `402 billing_required` or `spend_cap_reached`, `403 trial_machine_class_not_allowed`, `409 plan_not_priced`, or `429 limit_reached`. See [Usage, billing, and limits](/docs/guides/billing#limits).

- **API key:** `Authorization: Bearer vmy_…` with preset `use_existing`, `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner, Admin, or Operator.
- **Idempotency:** send a unique `Idempotency-Key`; replaying the same key and body returns the same operation.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `machine_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`EmptyAction`](/docs/api/schemas#emptyaction))

No fields.

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `202` | Accepted. Follow `operation_url` until the operation is terminal. | [`OperationAccepted`](/docs/api/schemas#operationaccepted) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `409` | `machine_deleted`: the machine's deletion was requested; `machine_not_created`: its create is pending or did not succeed, so only a delete is accepted | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `413` | The request body is too large (`body_too_large`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X POST "$VMYARD_API_URL/v1/machines/$MACHINE_ID/start" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```json title="Response 202"
{
  "machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_url": "/v1/operations/6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "requested_action": "start",
  "status": "pending"
}
```

## Stop a machine

`POST /v1/machines/{machine_id}/stop`

- **API key:** `Authorization: Bearer vmy_…` with preset `use_existing`, `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner, Admin, or Operator.
- **Idempotency:** send a unique `Idempotency-Key`; replaying the same key and body returns the same operation.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `machine_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`EmptyAction`](/docs/api/schemas#emptyaction))

No fields.

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `202` | Accepted. Follow `operation_url` until the operation is terminal. | [`OperationAccepted`](/docs/api/schemas#operationaccepted) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `409` | `machine_deleted`: the machine's deletion was requested; `machine_not_created`: its create is pending or did not succeed, so only a delete is accepted | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `413` | The request body is too large (`body_too_large`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X POST "$VMYARD_API_URL/v1/machines/$MACHINE_ID/stop" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```json title="Response 202"
{
  "machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_url": "/v1/operations/6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "requested_action": "stop",
  "status": "pending"
}
```

## Restart a machine

`POST /v1/machines/{machine_id}/restart`

Billing admission can refuse it: `402 billing_required` or `spend_cap_reached`, `403 trial_machine_class_not_allowed`, `409 plan_not_priced`, or `429 limit_reached`. See [Usage, billing, and limits](/docs/guides/billing#limits). One `restart` operation reboots the running machine without stopping it: `/work` is kept, its run stays open, and it succeeds only with a new `boot_id`. A host that can't restart its machines ends it `failed` with `restart_unsupported`, and the machine keeps running. See [Restart](/docs/concepts/lifecycle#restart).

- **API key:** `Authorization: Bearer vmy_…` with preset `use_existing`, `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner, Admin, or Operator.
- **Idempotency:** send a unique `Idempotency-Key`; replaying the same key and body returns the same operation.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `machine_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`EmptyAction`](/docs/api/schemas#emptyaction))

No fields.

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `202` | Accepted. Follow `operation_url` until the operation is terminal. | [`OperationAccepted`](/docs/api/schemas#operationaccepted) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `409` | `machine_deleted`: the machine's deletion was requested; `machine_not_created`: its create is pending or did not succeed, so only a delete is accepted | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `413` | The request body is too large (`body_too_large`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X POST "$VMYARD_API_URL/v1/machines/$MACHINE_ID/restart" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```json title="Response 202"
{
  "machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_url": "/v1/operations/6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "requested_action": "restart",
  "status": "pending"
}
```

## Delete a machine

`POST /v1/machines/{machine_id}/delete`

- **API key:** `Authorization: Bearer vmy_…` with preset `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner, Admin, or Operator.
- **Idempotency:** send a unique `Idempotency-Key`; replaying the same key and body returns the same operation.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `machine_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`EmptyAction`](/docs/api/schemas#emptyaction))

No fields.

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `202` | Accepted. Follow `operation_url` until the operation is terminal. | [`OperationAccepted`](/docs/api/schemas#operationaccepted) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `409` | `machine_deleted`: the machine's deletion was requested; `machine_not_created`: its create is pending or did not succeed, so only a delete is accepted | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `413` | The request body is too large (`body_too_large`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X POST "$VMYARD_API_URL/v1/machines/$MACHINE_ID/delete" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```json title="Response 202"
{
  "machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_url": "/v1/operations/6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "requested_action": "delete",
  "status": "pending"
}
```

## Change a machine's egress

`PUT /v1/machines/{machine_id}/network`

The machine must be running or stopped (`422` otherwise); the change never starts, stops, or restarts it. The `network_set` operation succeeds only when the host reports the requested policy applied, and `MachineDto.network` then shows it. A host without guest networking, or one whose operator turned internet egress off, ends it `failed` with nothing changed. See [Internet access and IPv6](/docs/guides/networking).

- **API key:** `Authorization: Bearer vmy_…` with preset `create_and_use`, `full_access`.
- **Browser session:** the `vmyard_session` cookie and the `x-csrf-token` header.
- **Who:** Owner, Admin, or Operator.
- **Idempotency:** send a unique `Idempotency-Key`; replaying the same key and body returns the same operation.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `machine_id` | path | `string (uuid)` | Yes |  |
| `X-Organization-ID` | header | `string (uuid)` | No | Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match. |

**Request body** (`application/json`, [`NetworkRequest`](/docs/api/schemas#networkrequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `egress` | [`EgressDto`](/docs/api/schemas#egressdto) | Yes |  |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `202` | The `network_set` operation changes the policy on the machine's host; the machine must be running or stopped. `MachineDto.network` shows the result once it succeeds. | [`OperationAccepted`](/docs/api/schemas#operationaccepted) |
| `401` | Not signed in, or the API key is malformed, expired, or revoked (`unauthenticated`, `invalid_api_key`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `403` | Refused for this caller: role, key preset, CSRF token, or organization (`forbidden`, `insufficient_scope`, `csrf_invalid`, `organization_mismatch`, `session_required`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `404` | Not found in this organization (`resource_not_found`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `409` | Conflicts with the current state, such as a reused idempotency key with another body (`idempotency_conflict`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

```sh title="Request"
curl -X PUT "$VMYARD_API_URL/v1/machines/$MACHINE_ID/network" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "egress": "none"
}'
```

```json title="Response 202"
{
  "machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "operation_url": "/v1/operations/6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "requested_action": "network_set",
  "status": "pending"
}
```
