DevPlace REST: Project Virtual Filesystem
The per-project file API: listing the tree, reading and writing text files, uploading binaries, mkdir, move and delete, with the relative POSIX path rules and the traversal rejections that guard them.
Each project carries a full virtual filesystem - directories and files - so a project can hold a complete software project. Reading is public (anyone can browse a project's tree); creating, editing, uploading, moving and deleting require the project owner. Text files are editable inline; binary files are uploaded and served from /static/uploads/project_files/....
Paths are relative POSIX paths inside the project (for example src/main.py). Parent directories are created automatically on write, upload and mkdir. Paths containing .., null bytes or empty segments are rejected.
Every endpoint follows the shared Conventions & Errors (auth, content negotiation, status codes); see Authentication for the four ways to sign requests.
GET /projects/{project_slug}/files - List a project's files
Return the flat list of files and directories in a project.
Minimal role: Public
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
Sample response
{
"project": {
"uid": "PROJECT_UID",
"slug": "PROJECT_SLUG"
},
"files": [
{
"path": "src/main.py",
"name": "main.py",
"type": "file",
"is_binary": false,
"size": 42
}
],
"is_owner": false
}
GET /projects/{project_slug}/files/raw - Read a project file
Return one file's metadata and (for text files) its content.
Minimal role: Public
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
path | query | string | yes | Relative file path inside the project. |
Sample response
{
"path": "src/main.py",
"name": "main.py",
"type": "file",
"is_binary": false,
"mime_type": "text/plain",
"size": 42,
"url": null,
"content": "print('hello')\n"
}
POST /projects/{project_slug}/files/write - Write a text file
Create or overwrite a text file; parent directories are created automatically.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
path | form | string | yes | Relative file path. |
content | form | textarea | yes | Full file content (max 400000 chars). |
Owner only; non-owners get
403. Invalid paths return400.
Sample response
{
"ok": true,
"redirect": "/projects/PROJECT_SLUG/files",
"data": {
"path": "src/main.py",
"type": "file"
}
}
GET /projects/{project_slug}/files/lines - Read a line range
Read a 1-indexed inclusive line range of a text file. Returns lines plus total_lines for targeting edits.
Minimal role: Public
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
path | query | string | yes | Relative file path. |
start | query | integer | no | First line, 1-indexed (default 1). |
end | query | integer | no | Last line inclusive; omit or -1 for end of file. |
Text files only; binary, directory, or missing paths return
404.
Sample response
{
"path": "src/main.py",
"start": 1,
"end": 2,
"total_lines": 2,
"lines": [
"import os",
"print(os.getcwd())"
],
"content": "import os\nprint(os.getcwd())"
}
POST /projects/{project_slug}/files/replace-lines - Replace a line range
Replace lines start..end (inclusive) with new content; empty content deletes the range. Leaves the rest of the file untouched.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
path | form | string | yes | Relative file path. |
start | form | integer | yes | First line to replace (1-indexed). |
end | form | integer | yes | Last line to replace (inclusive). |
content | form | textarea | no | Replacement text (empty deletes the range). |
Owner only. The preferred way to edit a large file; avoids rewriting the whole file.
Sample response
{
"ok": true,
"redirect": "/projects/PROJECT_SLUG/files",
"data": {
"path": "src/main.py",
"type": "file"
}
}
POST /projects/{project_slug}/files/insert-lines - Insert lines
Insert content before a 1-indexed line. Use at=1 to prepend and at=total_lines+1 to append.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
path | form | string | yes | Relative file path. |
at | form | integer | yes | Insert before this 1-indexed line. |
content | form | textarea | yes | Text to insert. |
Sample response
{
"ok": true,
"redirect": "/projects/PROJECT_SLUG/files",
"data": {
"path": "src/main.py",
"type": "file"
}
}
POST /projects/{project_slug}/files/delete-lines - Delete a line range
Delete lines start..end (inclusive) from a text file.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
path | form | string | yes | Relative file path. |
start | form | integer | yes | First line to delete (1-indexed). |
end | form | integer | yes | Last line to delete (inclusive). |
Sample response
{
"ok": true,
"redirect": "/projects/PROJECT_SLUG/files",
"data": {
"path": "src/main.py",
"type": "file"
}
}
POST /projects/{project_slug}/files/append - Append to a file
Append content as new lines at the end of a text file; grow a large file across calls without resending it.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
path | form | string | yes | Relative file path. |
content | form | textarea | yes | Text to append. |
Sample response
{
"ok": true,
"redirect": "/projects/PROJECT_SLUG/files",
"data": {
"path": "log.txt",
"type": "file"
}
}
POST /projects/{project_slug}/files/upload - Upload a file into a project
Upload a file into a directory (parents created); text decodes to an editable file, otherwise stored as binary.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
file | form | file | yes | The file to upload. |
path | form | string | no | Target directory, empty for the root. |
Sample response
{
"ok": true,
"redirect": "/projects/PROJECT_SLUG/files",
"data": {
"path": "assets/logo.png",
"type": "file",
"is_binary": true
}
}
POST /projects/{project_slug}/files/mkdir - Create a directory
Create a directory and any missing parents.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
path | form | string | yes | Relative directory path. |
Sample response
{
"ok": true,
"redirect": "/projects/PROJECT_SLUG/files",
"data": {
"path": "src/components",
"type": "dir"
}
}
POST /projects/{project_slug}/files/move - Move or rename
Move or rename a file or directory (and its descendants).
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
from_path | form | string | yes | Existing path. |
to_path | form | string | yes | New path. |
Sample response
{
"ok": true,
"redirect": "/projects/PROJECT_SLUG/files",
"data": {
"path": "src/new.py"
}
}
POST /projects/{project_slug}/files/delete - Delete a file or directory
Delete a file, or a directory and everything under it. Project owner or an administrator; soft-deleted (restorable from admin trash). Blocked while the project is read-only.
Minimal role: Member
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
path | form | string | yes | Relative path to delete. |
Sample response
{
"ok": true,
"redirect": "/projects/PROJECT_SLUG/files",
"data": {
"path": "src/old.py"
}
}
POST /projects/{project_slug}/files/zip - Queue a zip of files
Archive the whole tree, or a subtree via the path query. Returns the job uid and status URL to poll with /zips/{uid}.
Minimal role: Public
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_slug | path | string | yes | Project slug or uid. |
path | query | string | no | Relative file or directory to archive; empty for the whole project. |
Sample response
{
"uid": "ZIP_JOB_UID",
"status_url": "/zips/ZIP_JOB_UID"
}