# Background commands API

> Start detached commands, read their status and log, and kill them.

- [Start a background command](#start-a-background-command): `POST /v1/machines/{machine_id}/commands`
- [Get a background command](#get-a-background-command): `GET /v1/machines/{machine_id}/commands/{command_id}`
- [Kill a background command](#kill-a-background-command): `POST /v1/machines/{machine_id}/commands/{command_id}/kill`

## Start a background command

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

- **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`, [`StartCommandRequest`](/docs/api/schemas#startcommandrequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `argv` | `string[] \| null` | No | Program and arguments. Exactly one of `argv` or `command`. |
| `command` | `string \| null` | No | Shell command run as `/bin/sh -c <command>`, at most 8192 bytes. |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `202` | — | [`CommandAccepted`](/docs/api/schemas#commandaccepted) |
| `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) |
| `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/commands" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "command": "npm run dev > dev.log 2>&1"
}'
```

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

## Get a background command

`GET /v1/machines/{machine_id}/commands/{command_id}`

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

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `machine_id` | path | `string (uuid)` | Yes |  |
| `command_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. |
| `log_offset` | query | `integer` | No | Byte offset into the command log (default 0). |
| `log_limit` | query | `integer` | No | Bytes of log to return, 1 to 1048576 (default 65536). |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `200` | — | [`CommandDto`](/docs/api/schemas#commanddto) |
| `202` | The guest read is still queued; follow the operation | [`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) |
| `422` | Invalid request (`invalid_request`); `field_errors` names the fields. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `502` | The machine's host or a provider failed to answer. | [`ProblemDetails`](/docs/api/schemas#problemdetails) |

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

```json title="Response 200"
{
  "command_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "error_code": "string",
  "exit_code": 0,
  "log": {
    "data_base64": "string",
    "next_offset": 0,
    "offset": 0,
    "size": 0
  },
  "machine_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "signal": 0,
  "status": "starting"
}
```

## Kill a background command

`POST /v1/machines/{machine_id}/commands/{command_id}/kill`

- **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 |  |
| `command_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` | 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 POST "$VMYARD_API_URL/v1/machines/$MACHINE_ID/commands/$COMMAND_ID/kill" \
  -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": "kill",
  "status": "pending"
}
```
