# Files (/docs/api/files)



The files API operates on `/workspace`, the working filesystem inside a
live sandbox. Every path must resolve inside `/workspace`; anything else is
rejected with `path_outside_workspace`. Direct reads and writes are capped
at 256 KB per file; use download URLs for larger files.

## FileEntry and FileStat [#fileentry-and-filestat]

List, stat, and write operations return file records with this shape:

| Field        | Type    | Description                                |
| ------------ | ------- | ------------------------------------------ |
| `name`       | string  | Base name, when present                    |
| `path`       | string  | Absolute path inside the workspace         |
| `type`       | string  | `file` or `directory`                      |
| `sizeBytes`  | integer | Size in bytes                              |
| `modifiedAt` | string  | ISO 8601 modification time, when available |

## List a directory [#list-a-directory]

`GET /v1/sandboxes/{sandboxId}/files` — requires scope `file:read`.

Returns the entries of a directory.

| Parameter | Type   | Default      | Constraints                      |
| --------- | ------ | ------------ | -------------------------------- |
| `path`    | string | `/workspace` | Must resolve inside `/workspace` |

```bash title="Terminal"
curl "https://api.crownest.dev/v1/sandboxes/sbx_abc123/files?path=/workspace/src" \
  -H "Authorization: Bearer $CROWNEST_API_KEY"
```

```json
{
  "data": [
    {
      "name": "main.py",
      "path": "/workspace/src/main.py",
      "type": "file",
      "sizeBytes": 1204,
      "modifiedAt": "2026-06-11T13:00:00.000Z"
    }
  ]
}
```

## Stat a path [#stat-a-path]

`GET /v1/sandboxes/{sandboxId}/files/stat` — requires scope `file:read`.

Returns metadata for a single file or directory.

| Parameter | Type   | Required | Constraints                      |
| --------- | ------ | -------- | -------------------------------- |
| `path`    | string | Yes      | Must resolve inside `/workspace` |

```bash title="Terminal"
curl "https://api.crownest.dev/v1/sandboxes/sbx_abc123/files/stat?path=/workspace/src/main.py" \
  -H "Authorization: Bearer $CROWNEST_API_KEY"
```

Returns `200` with `{ "file": { ... } }`. Errors: `file_not_found`,
`path_outside_workspace`.

## Read a file [#read-a-file]

`GET /v1/sandboxes/{sandboxId}/files/read` — requires scope `file:read`.

Returns file content directly, up to 256 KB. Larger files return
`file_too_large` — fetch those through a
[download URL](#get-a-file-download-url) instead.

| Parameter  | Type   | Default | Constraints                   |
| ---------- | ------ | ------- | ----------------------------- |
| `path`     | string | —       | Required; inside `/workspace` |
| `encoding` | string | `utf8`  | `utf8` or `base64`            |

```bash title="Terminal"
curl "https://api.crownest.dev/v1/sandboxes/sbx_abc123/files/read?path=/workspace/results.json" \
  -H "Authorization: Bearer $CROWNEST_API_KEY"
```

```json
{
  "content": "{\"accuracy\": 0.97}",
  "encoding": "utf8"
}
```

Use `encoding=base64` for binary files.

## Write a file [#write-a-file]

`PUT /v1/sandboxes/{sandboxId}/files` — requires scope `file:write`.

Writes content to a path, up to 256 KB per request.

| Field           | Type    | Required | Default | Constraints                       |
| --------------- | ------- | -------- | ------- | --------------------------------- |
| `path`          | string  | Yes      | —       | Inside `/workspace`               |
| `content`       | string  | Yes      | —       | Max 256 KB                        |
| `encoding`      | string  | No       | `utf8`  | `utf8` or `base64`                |
| `overwrite`     | boolean | No       | `true`  | `false` fails if the file exists  |
| `createParents` | boolean | No       | `false` | Create missing parent directories |

```bash title="Terminal"
curl -X PUT https://api.crownest.dev/v1/sandboxes/sbx_abc123/files \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "path": "/workspace/src/config.json",
    "content": "{\"debug\": true}",
    "createParents": true
  }'
```

Returns `200` with `{ "file": { ... } }`. Errors: `file_too_large`,
`file_already_exists` (with `overwrite: false`),
`parent_directory_not_found` (with `createParents: false`),
`invalid_file_encoding`.

## Delete a file [#delete-a-file]

`DELETE /v1/sandboxes/{sandboxId}/files` — requires scope `file:write`.

Deletes a file or an empty directory. There is no recursive delete: a
non-empty directory returns `directory_not_empty`.

| Parameter | Type   | Required | Constraints         |
| --------- | ------ | -------- | ------------------- |
| `path`    | string | Yes      | Inside `/workspace` |

```bash title="Terminal"
curl -X DELETE "https://api.crownest.dev/v1/sandboxes/sbx_abc123/files?path=/workspace/tmp.txt" \
  -H "Authorization: Bearer $CROWNEST_API_KEY"
```

```json
{ "deleted": true }
```

## Create a directory [#create-a-directory]

`POST /v1/sandboxes/{sandboxId}/files/mkdir` — requires scope
`file:write`.

Creates a directory.

| Field     | Type    | Required | Default | Constraints                             |
| --------- | ------- | -------- | ------- | --------------------------------------- |
| `path`    | string  | Yes      | —       | Inside `/workspace`                     |
| `parents` | boolean | No       | `false` | Create missing intermediate directories |

```bash title="Terminal"
curl -X POST https://api.crownest.dev/v1/sandboxes/sbx_abc123/files/mkdir \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "path": "/workspace/data/raw", "parents": true }'
```

Returns `200` with `{ "file": { ... } }`.

## Move a file [#move-a-file]

`POST /v1/sandboxes/{sandboxId}/files/move` — requires scope `file:write`.

Moves or renames a file or directory.

| Field       | Type    | Required | Default | Constraints                        |
| ----------- | ------- | -------- | ------- | ---------------------------------- |
| `from`      | string  | Yes      | —       | Inside `/workspace`                |
| `to`        | string  | Yes      | —       | Inside `/workspace`                |
| `overwrite` | boolean | No       | `false` | `true` replaces an existing target |

```bash title="Terminal"
curl -X POST https://api.crownest.dev/v1/sandboxes/sbx_abc123/files/move \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "from": "/workspace/draft.md", "to": "/workspace/final.md" }'
```

Returns `200` with `{ "file": { ... } }`. Errors: `file_not_found`,
`file_already_exists` (with `overwrite: false`).

## Get a file download URL [#get-a-file-download-url]

`POST /v1/sandboxes/{sandboxId}/files/download-url` — requires scope
`file:read`.

Returns a short-lived URL for downloading a file of any size. This is the
path for files over the 256 KB direct read limit.

| Field  | Type   | Required | Constraints         |
| ------ | ------ | -------- | ------------------- |
| `path` | string | Yes      | Inside `/workspace` |

```bash title="Terminal"
curl -X POST https://api.crownest.dev/v1/sandboxes/sbx_abc123/files/download-url \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "path": "/workspace/dataset.csv" }'
```

```json
{
  "url": "https://api.crownest.dev/v1/...",
  "method": "GET",
  "authMode": "api_key"
}
```

## Download a file [#download-a-file]

`GET /v1/sandboxes/{sandboxId}/files/download` — requires scope
`file:read`.

Returns file bytes directly as `application/octet-stream`. This is the
same URL returned by the download-url endpoint.

| Parameter | Type   | Required | Constraints         |
| --------- | ------ | -------- | ------------------- |
| `path`    | string | Yes      | Inside `/workspace` |

```bash title="Terminal"
curl "https://api.crownest.dev/v1/sandboxes/sbx_abc123/files/download?path=/workspace/dataset.csv" \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  --output dataset.csv
```

Errors: `file_not_found`, `path_outside_workspace`.

## File error codes [#file-error-codes]

These codes are specific to file operations, in addition to the
[common error codes](/docs/api/errors):

| Code                         | Meaning                                       |
| ---------------------------- | --------------------------------------------- |
| `path_outside_workspace`     | Path resolves outside `/workspace`            |
| `path_is_directory`          | Expected a file, got a directory              |
| `path_not_directory`         | Expected a directory, got a file              |
| `file_not_found`             | No file or directory at the path              |
| `file_too_large`             | Content exceeds the 256 KB direct limit       |
| `file_already_exists`        | Target exists and `overwrite` is `false`      |
| `parent_directory_not_found` | Parent missing and `createParents` is `false` |
| `directory_not_empty`        | Directory has entries and can't be deleted    |
| `invalid_file_encoding`      | Content doesn't match the declared encoding   |

## Next steps [#next-steps]

* Keep files beyond the sandbox lifetime in
  [Artifacts](/docs/api/artifacts).
* Generate files with [Commands](/docs/api/commands).
