# Files API

> Write and read files inside a machine's /work directory.

- [Read a file](#read-a-file): `GET /v1/machines/{machine_id}/files`
- [Write a file](#write-a-file): `PUT /v1/machines/{machine_id}/files`

## Read a file

`GET /v1/machines/{machine_id}/files`

- **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 |  |
| `path` | query | `string` | Yes | `relative/path` or `/work/relative/path`; no `.`/`..` components. |
| `encoding` | query | [`FileEncodingDto`](/docs/api/schemas#fileencodingdto) | No | `text` (default) or `base64`. |
| `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` | — | [`FileContentDto`](/docs/api/schemas#filecontentdto) |
| `202` | The read is still queued; its operation carries `file_ranges` when done | [`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` | Unknown machine, or `file_not_found` | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `413` | `file_too_large`: the file exceeds 32 MiB | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `422` | Invalid path, or `file_not_text` for non-UTF-8 content read as text | [`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/files?path=notes/todo.txt" \
  -H "Authorization: Bearer $VMYARD_API_KEY"
```

```json title="Response 200"
{
  "content": "string",
  "encoding": "text",
  "path": "notes/todo.txt",
  "size": 0
}
```

## Write a file

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

- **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 |  |
| `path` | query | `string` | Yes | `relative/path` or `/work/relative/path`; no `.`/`..` components. |
| `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`, [`WriteFileRequest`](/docs/api/schemas#writefilerequest))

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `content` | `string` | Yes |  |
| `encoding` | [`FileEncodingDto`](/docs/api/schemas#fileencodingdto) | Yes |  |

**Responses**

| Status | Description | Body |
| --- | --- | --- |
| `202` | The file is replaced in runtime-sized chunks by 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) |
| `409` | Conflicts with the current state, such as a reused idempotency key with another body (`idempotency_conflict`). | [`ProblemDetails`](/docs/api/schemas#problemdetails) |
| `413` | `file_too_large`: the decoded file exceeds 32 MiB | [`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/files?path=notes/todo.txt" \
  -H "Authorization: Bearer $VMYARD_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "encoding": "text",
  "content": "hello from VMYard\n"
}'
```

```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": "file_write",
  "status": "pending"
}
```
