PBN.LTD API docs
View as Markdown

Files

Browse, read, upload, rename and delete the files of a site.

List a folder

GET/api/v1/sites/{site_id}/files

Scope files:read

Folders first, then files; up to 5,000 entries. Symbolic links are listed (type "link") but never followed. Available under the same rule as the File Manager: the site is installed, the plan is active and the site is not suspended.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
pathquerystringnoFolder path relative to the site folder (default: the site folder itself).

Example

curl -s "https://app.pbn.ltd/api/v1/sites/123/files?path=wp-content" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/files?path=wp-content", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/files?path=wp-content", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "path": "wp-content",
    "items": [
      {
        "name": "themes",
        "type": "folder",
        "size": null,
        "modified": "2026-09-01T10:00:00Z",
        "mode": "644",
        "path": "wp-content/themes"
      }
    ],
    "truncated": false
  }
}

Errors: conflict, forbidden, not_found, rate_limited, scope_missing, unauthorized

File or folder details

GET/api/v1/sites/{site_id}/files/stat

Scope files:read

Type, size, modification time and mode of one path.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
pathquerystringyesPath relative to the site folder, e.g. "wp-content/uploads" ("" or "/" = the site folder).

Example

curl -s "https://app.pbn.ltd/api/v1/sites/123/files/stat?path=index.php" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/files/stat?path=index.php", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/files/stat?path=index.php", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "name": "index.php",
    "type": "file",
    "size": 405,
    "modified": "2026-09-01T10:00:00Z",
    "mode": "644",
    "path": "index.php"
  }
}

Errors: not_found, rate_limited, scope_missing, unauthorized

Read (download) a file

GET/api/v1/sites/{site_id}/files/content

Scope files:read

Returns a file of up to 5 MB (the limit is in GET /limits). Use encoding=raw to stream the bytes directly.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
pathquerystringyesPath relative to the site folder, e.g. "wp-content/uploads" ("" or "/" = the site folder).
encodingquerystring (one of: base64, text, raw)nobase64 (JSON, any file), text (JSON, UTF-8 files) or raw (the bytes themselves, with a Content-Type). Default: base64.

Example

curl -s "https://app.pbn.ltd/api/v1/sites/123/files/content?path=robots.txt&encoding=text" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/files/content?path=robots.txt&encoding=text", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/files/content?path=robots.txt&encoding=text", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "path": "robots.txt",
    "size": 67,
    "modified": "2026-09-01T10:00:00Z",
    "content_type": "text/plain",
    "content": "User-agent: *\nDisallow:\n",
    "encoding": "text"
  }
}

Errors: not_found, rate_limited, scope_missing, too_large, unauthorized

Upload (write) a file

PUT/api/v1/sites/{site_id}/files/content

Scope files:write

Writes a file of up to 25 MB atomically (a temporary file renamed into place), owned by the site's web server user. Refuses to write through symbolic links and into folders the platform manages.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
pathquerystringyesWhere to write it, relative to the site folder.
contentbodystringnoText content (UTF-8). Or use content_base64.
content_base64bodystringnoThe file, base64-encoded. Or send the raw bytes as the request body (Content-Type application/octet-stream) or a multipart form with a "file" field.
overwritequerybooleannoReplace an existing file. Default: False.
mkdirsquerybooleannoCreate missing folders. Default: True.

Example

curl -s -X PUT "https://app.pbn.ltd/api/v1/sites/123/files/content?path=hello.txt&overwrite=true" \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "Hello world\n"}'
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.put("https://app.pbn.ltd/api/v1/sites/123/files/content?path=hello.txt&overwrite=true", headers=headers, json={"content": "Hello world\n"}, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/files/content?path=hello.txt&overwrite=true", {
  method: "PUT",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
  body: JSON.stringify({"content": "Hello world\n"})
});
console.log(res.status, await res.json());

Response

201

{
  "data": {
    "name": "hello.txt",
    "type": "file",
    "size": 12,
    "modified": "2026-09-01T10:00:00Z",
    "mode": "644",
    "path": "hello.txt",
    "written": 12
  }
}

Errors: conflict, forbidden, not_found, rate_limited, scope_missing, too_large, unauthorized

Create a folder

POST/api/v1/sites/{site_id}/files/folders

Scope files:write

Creates a folder (and any missing parent folders).

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
pathbodystringyesThe new folder path.

Example

curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/files/folders" \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"path": "wp-content/uploads/reports"}'
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/files/folders", headers=headers, json={"path": "wp-content/uploads/reports"}, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/files/folders", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
  body: JSON.stringify({"path": "wp-content/uploads/reports"})
});
console.log(res.status, await res.json());

Response

201

{
  "data": {
    "name": "new",
    "type": "folder",
    "size": null,
    "modified": "2026-09-01T10:00:00Z",
    "mode": "755",
    "path": "wp-content/new"
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized

Rename or move

POST/api/v1/sites/{site_id}/files/move

Scope files:write

Renames or moves a file or folder inside the site folder.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
pathbodystringyesWhat to move.
tobodystringyesThe new path.
overwritebodybooleannoReplace an existing file at the destination. Default: False.

Example

curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/files/move" \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"path": "hello.txt", "to": "old/hello.txt"}'
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/files/move", headers=headers, json={"path": "hello.txt", "to": "old/hello.txt"}, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/files/move", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
  body: JSON.stringify({"path": "hello.txt", "to": "old/hello.txt"})
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "name": "index.php",
    "type": "file",
    "size": 405,
    "modified": "2026-09-01T10:00:00Z",
    "mode": "644",
    "path": "index.php",
    "moved_from": "old.php"
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized

Delete a file or folder

DELETE/api/v1/sites/{site_id}/files

Scope files:write · destructive

Deletes a file, or a folder (with recursive=true when it is not empty). There is no undo - take a backup first for anything important.

Parameters

NameInTypeRequiredDescription
site_idpathintegeryesThe site id (see GET /sites).
pathquerystringyesPath relative to the site folder, e.g. "wp-content/uploads" ("" or "/" = the site folder).
recursivequerybooleannoRequired to delete a folder that is not empty. Default: False.

Example

curl -s -X DELETE "https://app.pbn.ltd/api/v1/sites/123/files?path=hello.txt" \
  -H "Authorization: Bearer $PBN_API_KEY"
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.delete("https://app.pbn.ltd/api/v1/sites/123/files?path=hello.txt", headers=headers, timeout=120)
print(r.status_code, r.json())
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/files?path=hello.txt", {
  method: "DELETE",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());

Response

200

{
  "data": {
    "path": "old.zip",
    "deleted": true,
    "entries_removed": 1
  }
}

Errors: conflict, not_found, rate_limited, scope_missing, unauthorized