Browse & Manage Remote Files
Explore a device's filesystem and manage folders remotely.
Before transferring, you often need to discover the exact paths on a device. This example browses a directory and shows how to create, rename, and delete folders on the remote device.
Steps
- Log in and pick a source device and a path.
- Browse the path with
POST /api/explorer/fileSearchV3/{deviceId}. - Create, rename, or delete folders as needed (opt-in in the code).
Configuration
Set your credentials and IDs via environment variables or a .env file (INNORIX_BASE_URL, INNORIX_WORKSPACE_ID, INNORIX_EMAIL, INNORIX_PASSWORD, and the INNORIX_SOURCE_ID / INNORIX_TARGET_ID / path variables the example uses).
API
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/explorer/fileSearchV3/{deviceId} | Browse files/folders on a device |
| POST | /api/explorer/createFolder/{deviceId} | Create a folder |
| POST | /api/explorer/renameFile/{deviceId} | Rename a file or folder |
| POST | /api/explorer/removeFile/{deviceId} | Delete files or folders |
python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""
03 - Remote file explorer
========================
Browse a source device's filesystem and manage folders remotely: create,
rename, and delete. Useful when you need to discover the exact file paths to
transfer instead of hard-coding them.
This file is self-contained: it embeds a minimal client so it can be read and
run on its own.
Endpoints:
POST /api/explorer/fileSearchV3/{deviceId}
POST /api/explorer/createFolder/{deviceId}
POST /api/explorer/renameFile/{deviceId}
POST /api/explorer/removeFile/{deviceId}
Run:
python 03_explorer.py
"""
from __future__ import annotations
import os
import json
import logging
import requests
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass
# --------------------------------------------------------------------------- #
# Configuration
# --------------------------------------------------------------------------- #
BASE_URL = os.getenv("INNORIX_BASE_URL", "https://app.innorix.com")
WORKSPACE_ID = os.getenv("INNORIX_WORKSPACE_ID", "<WORKSPACE_ID>")
EMAIL = os.getenv("INNORIX_EMAIL", "<YOUR_EMAIL>")
PASSWORD = os.getenv("INNORIX_PASSWORD", "<YOUR_PASSWORD>")
SOURCE_ID = os.getenv("INNORIX_SOURCE_ID", "<SOURCE_DEVICE_ID>")
SOURCE_PATHS = [p.strip() for p in os.getenv(
"INNORIX_SOURCE_PATHS", "C:/Users/innorix/Downloads/image").split(",") if p.strip()]
logging.basicConfig(level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
datefmt="%H:%M:%S")
log = logging.getLogger("innorix")
# --------------------------------------------------------------------------- #
# Minimal client
# --------------------------------------------------------------------------- #
class ApiError(RuntimeError):
"""Raised when the API returns a non-2xx response."""
class InnorixClient:
def __init__(self, base_url: str, workspace_id: str, timeout: float = 15.0):
self.base_url = base_url.rstrip("/")
self.workspace_id = workspace_id
self.timeout = timeout
self.session = requests.Session()
def _request(self, method: str, path: str, **kwargs):
kwargs.setdefault("timeout", self.timeout)
res = self.session.request(method, f"{self.base_url}{path}", **kwargs)
try:
res.raise_for_status()
except requests.HTTPError as exc:
raise ApiError(f"{method} {path} -> {res.status_code}: {res.text[:500]}") from exc
if not res.content:
return None
try:
return res.json()
except ValueError:
return res.text
def login(self, email: str, password: str) -> str:
data = self._request("POST", "/api/auth/login",
json={"email": email, "password": password}, timeout=10)
token = data["data"]["user"]["accessToken"]
self.session.headers.update({
"Authorization": f"Bearer {token}",
"x-workspace-id": self.workspace_id,
"Content-Type": "application/json",
})
log.info("logged in")
return token
def browse(self, device_id: str, path: str, only_folder: bool = False):
"""POST /api/explorer/fileSearchV3/{deviceId}."""
return self._request("POST", f"/api/explorer/fileSearchV3/{device_id}",
json={"path": path, "onlyFolder": only_folder})
def create_folder(self, device_id: str, path: str):
return self._request("POST", f"/api/explorer/createFolder/{device_id}",
json={"path": path})
def rename_file(self, device_id: str, path: str, new_name: str):
return self._request("POST", f"/api/explorer/renameFile/{device_id}",
json={"path": path, "name": new_name})
def remove_files(self, device_id: str, list_files: list):
return self._request("POST", f"/api/explorer/removeFile/{device_id}",
json={"listFiles": list_files})
# --------------------------------------------------------------------------- #
# Run
# --------------------------------------------------------------------------- #
def main() -> None:
client = InnorixClient(BASE_URL, WORKSPACE_ID)
client.login(EMAIL, PASSWORD)
device_id = SOURCE_ID
base = SOURCE_PATHS[0]
# 1) Browse a path (set only_folder=True to list folders only).
listing = client.browse(device_id, base, only_folder=False)
print(json.dumps(listing, indent=2, ensure_ascii=False)[:1000])
# --- The mutating calls below are commented out on purpose. -------------
# Uncomment and adjust paths to actually create/rename/delete on the device.
# 2) Create a folder.
# client.create_folder(device_id, f"{base}/example_new_folder")
# 3) Rename it (path = current path, name = new leaf name).
# client.rename_file(device_id, f"{base}/example_new_folder", "renamed_folder")
# 4) Remove files/folders (listFiles = absolute paths).
# client.remove_files(device_id, [f"{base}/renamed_folder"])
if __name__ == "__main__":
main()