> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instapods.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Files

> Browse, read, write, upload, and manage files inside pods.

All file endpoints operate within the pod's filesystem. Paths must be absolute and within `/home/instapod`, `/var/www`, or `/tmp`.

## List Files

```
GET /api/pods/{name}/files?path=/home/instapod/app
```

| Query Param | Type | Default | Description |
| - | - | - | - |
| `path` | string | Pod's app root | Directory to list |

**Response: `200 OK`**

```json theme={null}
{
  "path": "/home/instapod/app",
  "files": [
    {
      "name": "index.js",
      "permissions": "-rw-r--r--",
      "size": "1234",
      "is_dir": false
    },
    {
      "name": "node_modules",
      "permissions": "drwxr-xr-x",
      "size": "4096",
      "is_dir": true
    }
  ]
}
```

Returns an empty `files` array if the directory doesn't exist.

## Read a File

```
GET /api/pods/{name}/files/content?path=/home/instapod/app/index.js
```

| Query Param | Type | Required | Description |
| - | - | - | - |
| `path` | string | Yes | Absolute file path |

**Response: `200 OK`**

```json theme={null}
{
  "path": "/home/instapod/app/index.js",
  "content": "const express = require('express');\n..."
}
```

This endpoint returns text. It responds `413` for a file over 5 MB and `415` for a file that is not UTF-8 text (binary, or text in another encoding). Use [Download a File](#download-a-file) for those.

## Write a File

```
PUT /api/pods/{name}/files/content
```

**Request Body:**

```json theme={null}
{
  "path": "/home/instapod/app/index.js",
  "content": "const express = require('express');\n..."
}
```

**Response: `200 OK`**

```json theme={null}
{
  "status": "saved",
  "path": "/home/instapod/app/index.js"
}
```

Parent directories are created automatically if they don't exist.

## Upload a File

```
POST /api/pods/{name}/files/upload
```

**Request:** `multipart/form-data`

| Field | Type | Required | Description |
| - | - | - | - |
| `file` | File | Yes | File to upload (max 32 MB) |
| `path` | string | No | Target directory (default: app root) |

**Response: `200 OK`**

```json theme={null}
{
  "status": "uploaded",
  "path": "/home/instapod/app/style.css"
}
```

## Download a File

```
GET /api/pods/{name}/files/download?path=/home/instapod/app/data.json
```

Returns the raw file contents as binary with `Content-Disposition: attachment`. The file is streamed rather than buffered, so large files are fine.

## Delete a File

```
DELETE /api/pods/{name}/files?path=/home/instapod/app/old-file.js
```

**Response: `200 OK`**

```json theme={null}
{
  "status": "deleted",
  "path": "/home/instapod/app/old-file.js"
}
```

Works on both files and directories (recursive delete).

## Rename / Move

```
POST /api/pods/{name}/files/rename
```

**Request Body:**

```json theme={null}
{
  "old_path": "/home/instapod/app/old-name.js",
  "new_path": "/home/instapod/app/new-name.js"
}
```

**Response: `200 OK`**

```json theme={null}
{
  "status": "renamed",
  "old_path": "/home/instapod/app/old-name.js",
  "new_path": "/home/instapod/app/new-name.js"
}
```

## Copy

```
POST /api/pods/{name}/files/copy
```

**Request Body:**

```json theme={null}
{
  "source_path": "/home/instapod/app/config.js",
  "dest_path": "/home/instapod/app/config.backup.js"
}
```

**Response: `200 OK`**

```json theme={null}
{
  "status": "copied",
  "source_path": "/home/instapod/app/config.js",
  "dest_path": "/home/instapod/app/config.backup.js"
}
```

Works recursively for directories.

## Create Folder

```
POST /api/pods/{name}/folders
```

**Request Body:**

```json theme={null}
{
  "path": "/home/instapod/app/src/components"
}
```

**Response: `200 OK`**

```json theme={null}
{
  "status": "created",
  "path": "/home/instapod/app/src/components"
}
```

Creates the full directory tree (equivalent to `mkdir -p`).

***

## Sync Endpoints

Three endpoints exist for uploading a whole project efficiently. They are what
[`instapods deploy`](/cli/deploy) uses, and they're documented here because anything that syncs a
directory wants the same three steps: compare cheaply, confirm what looks unchanged, then upload the
rest in one request.

### File Manifest

```
GET /api/pods/{name}/files/manifest?path=/home/instapod/app
```

A recursive listing of every file under a directory with its size, as a flat map of path relative to
`path` to bytes.

| Query Param | Type | Default | Description |
| - | - | - | - |
| `path` | string | Pod's app root | Directory to walk |

**Response: `200 OK`**

```json theme={null}
{
  "path": "/home/instapod/app",
  "files": {
    "index.js": 1234,
    "src/app.js": 5678,
    "public/style.css": 910
  }
}
```

A directory that doesn't exist yet returns an empty `files` object, not a `404`.

### File Hashes

```
POST /api/pods/{name}/files/hashes
```

The SHA-256 of specific files. Sizes alone can't tell a same-length edit from no edit at all, so ask
for hashes of exactly the files whose size matched the manifest rather than hashing the whole tree.

```json theme={null}
{
  "path": "/home/instapod/app",
  "files": ["index.js", "src/app.js"]
}
```

| Field | Type | Default | Description |
| - | - | - | - |
| `path` | string | Pod's app root | Directory the entries are relative to |
| `files` | array | - | Paths relative to `path` |

**Response: `200 OK`**

```json theme={null}
{
  "path": "/home/instapod/app",
  "hashes": {
    "index.js": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
  }
}
```

A file that is missing or unreadable is simply **absent** from `hashes` rather than reported as an
error. Treat an absent hash as "changed" and re-upload it; that's the safe direction, and it's what
the CLI does.

### Upload an Archive

```
POST /api/pods/{name}/files/upload-archive
```

Uploads a `.tar.gz` and extracts it into a directory on the pod, replacing what would otherwise be
one request per file.

**Request:** `multipart/form-data`

| Field | Type | Required | Description |
| - | - | - | - |
| `archive` | File | Yes | A gzipped tar archive. Max 128 MB |
| `path` | string | No | Target directory (default: app root) |

```bash theme={null}
tar czf changed.tar.gz -C ./dist .
curl -X POST https://app.instapods.com/api/pods/my-app/files/upload-archive \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "archive=@changed.tar.gz"
```

**Response: `200 OK`**

```json theme={null}
{
  "status": "uploaded",
  "path": "/home/instapod/app"
}
```

The archive is extracted **over** the target directory: existing files with the same path are
overwritten, and files not in the archive are left alone. It is not a mirror, so it never deletes.
Ownership under `/home/instapod` is fixed up afterwards.

***

## Path Validation

All paths must be:

* **Absolute** (start with `/`)
* **Within allowed directories**: `/home/instapod`, `/var/www`, or `/tmp`
* **Free of traversal** (no `..` segments)

Invalid paths return `400 Bad Request`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.