Find and Transfer Files Across Multiple Devices from One Place

Browse files and folders across connected systems from one unified explorer and perform search, access, and file-transfer operations.

IT EngineersDevelopers

Getting Started#

Basic Concept#

View files across multiple devices in one place

Unified File Explorer connects multiple devices so you can browse the files and folders on each device from a single interface.

Users can select a connected device, browse its folders, locate the files they need, and work with files across multiple devices from one workspace.

For example, you can view documents on a work PC, data on a server, and result files in storage from the same browsing environment and select the files you need.

Exploration Flow#

Move from device selection to file review and transfer

Unified File Explorer provides a workflow for finding files on connected devices and transferring selected files to the next work environment.

  1. Select a device
  2. Browse folders
  3. Review files
  4. Select the required files
  5. Select a transfer target
  6. Transfer files
  7. Review results

This flow takes you from locating files to transferring them to the required work location and reviewing the results.

Workflow Changes#

Connect file browsing and transfer in a single workflow

When work depends on files across multiple devices, users must identify where the required files are stored, locate them on the relevant device, and prepare them for the next work environment.

With Unified File Explorer, users can find the files they need on connected devices and transfer selected files directly to a specified device or workspace.

Category File Management by Device Unified File Explorer
Start Work Identify the device containing the required files Select directly from the connected device list
Browse Files Review files and folders in each device environment Browse devices and files from one screen
Prepare Files Prepare files for the next work location after reviewing them Transfer selected files directly to the target device
Continue Work Continue to the next task after preparing files Use transferred files immediately after completion

By combining file browsing and transfer into one flow, files across multiple work environments can be used directly where they are needed.

IT Engineers#

Configure and manage a file browsing environment across multiple devices

Connect Devices#

Connect devices for browsing and expand the environment as needed

To configure Unified File Explorer, first connect the PCs, servers, storage systems, and other devices that contain the files you need to access.

After configuring the connection information for each device, you can browse that system's files and folders from the unified explorer.

As the work environment expands, you can add new servers or storage systems in the same way. After configuring their connection information, the new devices can be included in the existing browsing environment.

Device Type Files Used
Work PC Personal and team work files
Windows Server Business documents and operational files
Linux Server Data and processing files
Storage Shared files and result files

Adding devices extends the browsing scope to additional systems while preserving the existing file exploration environment.

Access Scope#

Define which files and folders users can access on each device

After connecting devices, configure which devices and file paths users can access based on their roles and responsibilities.

You can assign devices to individual users or user groups and define the folder scope available on each device.

For example, the operations team can be given access to designated folders on operational servers, while the data team can access work paths on analytics servers and data storage.

User Group Devices to Browse File Scope
Operations Team Operations Server Operational file paths
Data Team Analytics Server Data folders
Business Team Shared Storage Work file folders

Defining access scopes by user lets you operate Unified File Explorer around the devices and files each team needs for its work.

Browse Files#

Find the files you need across connected devices

After devices and access scopes are configured, users can select a device in the unified explorer and browse its folders and files.

Selecting a system from the device list displays its folder structure and file list, allowing users to locate files by name and path.

The file browser displays the following information:

Item Description
Device Device containing the file
Path Current file path
File Name File name
Size File size
Modified Last modified time

After locating a file, select it to continue directly to the next transfer operation.

File Transfer#

Transfer selected files to the required device or workspace

After selecting the required files in the explorer, specify the destination device and target path.

The selected files are transferred from their current location to the specified target device or workspace, where they can be reviewed and used in the next task.

The transfer flow is structured as follows:

Device A
Browse files
Select files
Select target
Device B
Workspace

Finding files and selecting a destination from the same explorer connects file browsing and transfer in a single workflow.

Verify Results#

Review transfer status and file processing results

When a file transfer runs, you can review its progress and processing results in Runs.

Each run shows the Source and Target for the transferred files, file count, transfer volume, progress, execution time, and current status.

Item Details
Source Device and path where the files were selected
Target Device and workspace receiving the files
Files Number of processed files
Size Total transfer volume
Progress Current transfer progress
Status Current run status
Time Execution and completion times

Operators can use run results to review file transfer flows and processing status between devices and manage how files are delivered to each work environment.

Developers#

Retrieve file lists and search results from remote devices through the API and run transfers between devices

Integration Setup#

Prepare shared API calls and status values

import os
import requests

BASE_URL = os.getenv("INNORIX_BASE_URL", "https://app.innorix.com").rstrip("/")
TOKEN = os.environ["INNORIX_ACCESS_TOKEN"]
WORKSPACE_ID = os.getenv("INNORIX_WORKSPACE_ID")   # optional; falls back to the current workspace

STATUS_COMPLETE = 2
TERMINAL = {2, 4, 5, 9, 99}          # complete / error / cancelled / partial / failed
NOT_SUCCEEDED = {4, 5, 9, 99}


def api(method, path, body=None, params=None):
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {TOKEN}",
    }

    if WORKSPACE_ID:
        headers["x-workspace-id"] = WORKSPACE_ID

    response = requests.request(
        method, BASE_URL + path,
        headers=headers, json=body, params=params, timeout=30,
    )

    payload = response.json() if response.content else {}

    if not response.ok:
        raise RuntimeError(payload.get("message") or f"HTTP {response.status_code}")

    return payload.get("data")


def is_terminal(detail):
    return detail.get("isTerminal", detail.get("status") in TERMINAL)

Use the following values to determine transfer status. There are five terminal states, and Complete (2) is the successful state.

Status Value Meaning Terminal
2 Complete Yes
4 Error Yes
5 Cancelled Yes
9 Partial Complete Yes
99 Failed Yes
1, 6, 12, 13 Starting, Transferring, Synchronizing, Receiving No

List Folder Contents#

Retrieve a device file list and display it in the UI

Specify a device identifier and path to retrieve the files and subfolders in that folder. Because the value passed to --device is used as a path parameter, it must be a device ID.

result = api("GET", f"/api/devices/{device_id}/files", params={
    "path": "/data/reports",
    "page": 1,
    "size": 50,
    "sort": "name:asc",
    "type": "all",
})

for item in result["items"]:
    kind = "DIR " if item["isDir"] else "FILE"
    print(kind, item["name"], item["size"], item.get("modifiedAt"))
Response Field Description
items List of files and folders
total, lastPage Total item count and final page
truncated Whether only part of the result was returned because the item limit was exceeded
isDir Whether the item is a folder

Use the device list endpoint to find the device ID.

result = api("GET", "/api/devices", params={"page": 1, "size": 200})

for device in result["devices"]:
    print(device["deviceId"], device["name"], device.get("os"),
          device.get("ipAddress"))

The device list is returned in the data.devices array.

Search recursively through subfolders to find files

Folder listing returns only the current folder. To search through subfolders, start a search and retrieve subsequent results using the cursor.

RESTART_CODES = {"INVALID_CURSOR", "CURSOR_OUT_OF_SEQUENCE", "SEARCH_EXPIRED"}


def start_search(device_id, path, page_size=500):
    return api("POST", f"/api/devices/{device_id}/files/search",
               {"path": path, "pageSize": page_size})


def iter_search(device_id, path, max_pages=200):
    page = start_search(device_id, path)
    search_id = page.get("searchId")

    for _ in range(max_pages):
        for item in page.get("items") or []:
            yield search_id, item

        if not page.get("hasMore"):
            return

        try:
            page = api("GET", f"/api/devices/{device_id}/files/search",
                       params={"cursor": page["nextCursor"]})
        except RuntimeError:
            # restart the scan when the cursor expires or falls out of sequence
            page = start_search(device_id, path)
            search_id = page.get("searchId")

Search requests accept only the base path and page size. Name and extension filters are applied to the returned results, so using a narrower base path reduces the search scope.

Response Field Description
searchId Search identifier passed when cancelling a search
items[].type file or directory
hasMore, nextCursor Whether another page exists and the cursor used to retrieve it

Stop a Search#

Stop an active search

A search causes the device to scan its disk. Cancel the search when the user leaves the screen to prevent unnecessary load from accumulating on the device.

def cancel_search(device_id, search_id):
    api("POST", f"/api/devices/{device_id}/files/search/cancel",
        {"uuid": search_id})       # pass the searchId returned when the search started


search_id = None

try:
    for search_id, item in iter_search(device_id, "/data"):
        if item["type"] == "file" and item["name"].endswith(".csv"):
            print(item["path"], item["size"])
finally:
    if search_id:
        cancel_search(device_id, search_id)

Run the same cleanup logic both when starting a new search and when closing the screen.

File Transfer#

Send selected files to another device

For an immediate transfer, a device can be specified by name, IP address, or identifier, and paths are passed as plain-text strings. This differs from the browsing API, which accepts only device IDs.

When sending a list of files, use sourceItem with isDir: false instead of sourcePaths. sourcePaths treats every path as a folder, so passing files causes the server to scan each file as a folder, which can slow the request or cause it to time out.

def send_files(source, target, target_path, paths, action="numbering"):
    transfer = api("POST", "/api/transfers/manual", {
        "sourceDevice": source,
        "targetDevice": target,
        "targetPath": target_path,
        "sourceItem": [{"path": p, "isDir": False} for p in paths],
        "sendAllFolder": False,
        "transferOptions": {"target-action": action},
    })

    return transfer["monitorId"]


monitor_id = send_files("device-a", "device-b", "/data/collected",
                        ["/data/reports/2026-08.csv"])

When sending folders, use sourcePaths with sendAllFolder: True.

api("POST", "/api/transfers/manual", {
    "sourceDevice": "device-a",
    "targetDevice": "device-b",
    "targetPath": "/data/collected",
    "sourcePaths": ["/data/reports"],
    "sendAllFolder": True,
    "transferOptions": {"target-action": "numbering"},
})

If the file size is already known, pass fileSize to skip the server's per-item lookup.

json
"sourceItem": [
  { "path": "/data/a.csv", "isDir": false, "fileSize": 1200 }
]

Verify Results#

Review transfer status and per-file processing results

import time


def wait(monitor_id, timeout=1800, interval=3):
    deadline = time.time() + timeout

    while time.time() < deadline:
        detail = api("GET", f"/api/transfers/{monitor_id}")

        if is_terminal(detail):
            return detail

        time.sleep(interval)

    raise TimeoutError(monitor_id)


detail = wait(monitor_id)
succeeded = detail["status"] == STATUS_COMPLETE

Full failures and partial failures should be displayed differently in the UI. Check each file's status through the file listing endpoint and retry only the failed files.

def failed_files(monitor_id):
    result = api("GET", f"/api/transfers/{monitor_id}/files", params={
        "state": "any", "size": 500,
    }) or {}

    return [r for r in (result.get("children") or [])
            if r.get("status") in NOT_SUCCEEDED]


def retry_failed(monitor_id):
    rows = failed_files(monitor_id)

    if not rows:
        return 0

    api("POST", f"/api/transfers/{monitor_id}/retry", {
        "filesRetry": [
            {"filePath": r["sourceFilePath"], "isDir": bool(r.get("isFolder"))}
            for r in rows
        ]
    })

    return len(rows)

Retries can be requested only after the transfer reaches a terminal state. A retry is rejected while the transfer is still in progress, so check the status first.

Item Details
status Transfer status value
statusLabel Status string displayed in the UI
percent Progress percentage
fileCount, totalSize Number of processed files and total size
children[].errorCode Per-file failure reason