Getting Started
Basic Concepts
Migrate data from existing file environments to cloud storage
Enterprise files are distributed across various environments, including NAS, SMB shared folders, NFS storage, and file servers.
Cloud migration consists of transferring existing files to designated cloud storage and continuously applying changes that occur during the migration period to manage file state until the final cutover.
Existing File Environment
│
▼
Initial File Migration
│
▼
Changed Files Synchronization
│
▼
Check Cutover Status
│
▼
Cloud Cutover

By connecting the initial file migration with changed-file synchronization, you can migrate data from the existing file environment to the cloud and manage file state until the cutover.
Migration Flow
Continue from initial file migration through changed-file synchronization to final cutover
File migration consists of transferring the initial data to the cloud and continuously applying files created or modified during the migration period.
① Source Environment Connection
│
▼
② Initial File Migration
│
▼
③ Detect Changes
│
▼
④ Changed Files Synchronization
│
▼
⑤ Check Cutover Status
│
▼
⑥ Cloud Cutover
This flow lets you manage the initial files and subsequent changes together while proceeding with the cloud cutover.
Cutover Benefits
Continue the existing file flow into the cloud environment
Configuring initial migration and change synchronization together lets you manage file state across the source environment and cloud throughout the cutover period.
| Category | Existing File Environment | Cloud Cutover Flow |
|---|---|---|
| Initial Data | Managed in the existing environment | Migrate large volumes of files to the cloud |
| Changed Files | Check status by file | Continuously synchronize changes |
| Cutover Preparation | Check file state by environment | Verify based on migration and synchronization results |
| Subsequent Work | Use the existing storage location | Connect based on the cloud storage location |
IT Engineers
Source Environment
Connect migration paths from NAS and file servers
First, connect the NAS, SMB, NFS, or file server where the files to be migrated are stored, and specify the file paths to migrate to the cloud.
Set the access scope so files can be read and changes can be checked in each source environment.
NAS ─────────┐
SMB ─────────┤
NFS ─────────┼──→ Migration Flow
File Server ─┘

By connecting multiple source environments, you can configure migration tasks for each file location in a single flow.
Cloud Connection
Configure cloud storage and target paths
After connecting the source environment, configure the cloud storage and target location where files will be stored.
Specify the target bucket or container and storage path, and connect them to the source file path.
Original Storage
│
▼
Migration Flow
│
▼
Cloud Storage
└─ Target Path

Configuring the source paths to migrate and cloud storage locations prepares the file flow for initial migration and change synchronization.
Initial Migration
Transfer existing large volumes of files to the cloud
After configuring the migration environment, transfer existing files and large datasets to cloud storage.
During execution, you can check the number of processed files, total capacity, progress, and task status.
Original Files
│
▼
Initial Transfer
│
▼
Cloud Storage

Use the initial migration results as the baseline for subsequent changed-file synchronization and final cutover.
Change Synchronization
Continuously apply files changed during the migration period
Even after the initial migration, new files may be created or existing files may be modified in the original file environment.
Use New ItemsandModified Items` to identify changes and configure those files to be synchronized to cloud storage.
Original Environment
│
New / Modified
│
▼
Change Detection
│
▼
Sync
│
▼
Cloud Storage

By continuously applying changed files, you can continue managing file state in the cloud after the initial migration.
Cutover Verification
Verify initial migration and change-synchronization results
Before the final cutover, check the initial file migration results together with the synchronization status of subsequently changed files.
You can verify cutover readiness between the source and cloud environments based on file count and capacity, recent execution results, and synchronization status.
Initial Transfer ──┐
├──→ Migration Status
Change Sync ───────┘
│
┌────┴────┐
▼ ▼
Ready Check Items

Reviewing the initial migration and change-synchronization results together lets you determine when to switch operations to the cloud environment.
Final Cutover
Switch file operations to the cloud environment
After cutover verification is complete, set cloud storage as the baseline environment for subsequent file operations.
Connect the storage locations of business systems, applications, and subsequent file tasks to the cloud to continue the file flow.
Before
NAS / File Server
│
▼
Migration Flow
│
▼
Cloud Storage
│
▼
After
App / Service / Workflow

After migration, you can connect subsequent work and automation tasks based on files stored in the cloud.
Operations Response
Review and rerun migration and synchronization tasks
For tasks requiring review during migration, use Runs and detailed execution records to check source connectivity, target paths, file-processing results, and synchronization status.
Migration Run
│
▼
Status Check
│
┌────┴─────┐
▼ ▼
Complete Check Items
│
▼
View Details
│
▼
Update Settings
│
▼
Retry
│
▼
Result Confirm

| Check Item | Details | Follow-up Action |
|---|---|---|
| Source Environment | NAS, SMB, NFS, and File Server Connections and Paths | Check connection and path |
| Cloud | Storage location and access status | Check target settings |
| Initial Migration | Large-file processing results | Rerun the required task |
| Change Synchronization | Status of added and modified files | Rerun the synchronization task |
| Execution History | Task status and detailed processing results | Check settings and rerun |
Developers
Move an existing shared folder to the cloud and continuously apply changes during the migration period
Integration Setup
Prepare common API-call code and path notation
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)import base64
import time
def encode_path(device_id, raw_path):
normalized = str(raw_path or "").replace("\\", "/")
token = base64.b64encode(normalized.encode("utf-8")).decode("ascii")
return f"{device_id}_ino_{token}"
def now_iso():
return time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime())Transfer status is determined by the values below. There are five terminal states, and the successful value is Complete (2).
| Status Value | Meaning | Terminal |
|---|---|---|
| 2 | Complete | Yes |
| 4 | Error | Yes |
| 5 | Canceled | Yes |
| 9 | Partially Complete | Yes |
| 99 | Failed | Yes |
| 1 , 6 , 12 , 13 | Starting, Transferring, Synchronizing, Receiving | No |
Determine the Migration Scope
Check the number and capacity of files to migrate in advance
You need to know the scale before starting the migration so you can plan the schedule.
def scan(device_id, path, max_pages=200):
page = api("POST", f"/api/devices/{device_id}/files/search",
{"path": path, "pageSize": 1000})
total = size = 0
search_id = page.get("searchId")
try:
for _ in range(max_pages):
for item in page.get("items") or []:
if item["type"] == "file":
total += 1
size += item.get("size") or 0
if not page.get("hasMore"):
break
page = api("GET", f"/api/devices/{device_id}/files/search",
params={"cursor": page["nextCursor"]})
finally:
# stop the device scan once the count is done
if search_id:
api("POST", f"/api/devices/{device_id}/files/search/cancel",
{"uuid": search_id})
return total, size
count, size = scan("device-nas-01", "/share/team")
print(f"{count} files / {size / (1024 ** 3):.1f}GB")A search actually scans the device's disk. If it is not stopped, scanning continues even after the check is complete.
Initial Migration
Send existing large volumes of files to the cloud
Sending everything at once increases the scope that must be restarted after a failure. Split the transfer by top-level folder.
def target_for(folder, base):
return folder.replace("/share", base, 1)
def migrate(source, target, folder, base):
transfer = api("POST", "/api/transfers/manual", {
"sourceDevice": source,
"targetDevice": target,
"targetPath": target_for(folder, base),
"sourcePaths": [folder],
"sendAllFolder": True,
"transferOptions": {"target-action": "overwrite"},
})
return transfer["monitorId"]
FOLDERS = ["/share/team/design", "/share/team/docs", "/share/team/archive"]
transfers = {
folder: migrate("device-nas-01", "device-cloud-01", folder, "/migrated")
for folder in FOLDERS
}Use the overwrite destination policy. Reruns are common during migration, and numbering causes copies to accumulate.
Folders containing very large numbers of files can take a long time to transfer. If interrupted, the transfer can resume, so there is no need to retransmit from the beginning.
def wait(monitor_id, timeout=3600, 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)
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)for folder, monitor_id in transfers.items():
detail = wait(monitor_id, timeout=86400)
print(f"{folder:28} {detail.get('statusName')}"
f" {detail.get('fileCount')} files")
if detail["status"] != STATUS_COMPLETE:
print(f" retried {retry_failed(monitor_id)} files")Changes Synchronization
Continuously apply files changed during the migration period
Files continue to change at the source even while the initial migration takes several days. Send only the changes periodically.
transfer = api("POST", "/api/transfers/manual", {
"sourceDevice": "device-nas-01",
"targetDevice": "device-cloud-01",
"targetPath": "/migrated/team/design",
"sourcePaths": ["/share/team/design"],
"sendAllFolder": True,
"incremental": True,
"transferOptions": {"target-action": "overwrite"},
})The default is off. The device agent calculates changes and sends only files added or modified since the last transfer.
Always use overwrite for incremental transfers. With numbering, modified files accumulate under new names, causing the cloud state to differ from the source.
If you cannot keep the script running, register it as hourly automation.
def build_sync(name, source, source_path, target, target_path, schedule):
return {
"name": name,
"flowName": name,
"transferType": "normal",
"timezone": "Asia/Seoul",
"step": 1,
"isUpcoming": False,
"details": [
{
"senderId": source,
"receiverId": target,
"sourceItem": [
{
"hash": encode_path(source, source_path),
"filePath": source_path,
"isDir": True,
}
],
"targetPath": encode_path(target, target_path),
"step": 1,
"transferOptions": {
"noSchedule": False,
"target-action": "overwrite",
"send-fileoption": {},
},
}
],
"schedules": [schedule],
}
HOURLY = {
"type": "hour",
"startDateType": "now",
"startDate": now_iso(),
"timezone": "Asia/Seoul",
}
syncs = {
folder: api("POST", "/api/automations", build_sync(
f"sync {folder}", "device-nas-01", folder,
"device-cloud-01", target_for(folder, "/migrated"),
HOURLY))["automationId"]
for folder in FOLDERS
}There are four items that must be observed in automation requests.
| Item | Configuration |
|---|---|
isUpcoming | Must be false. The server default true ignores the schedule in the request and replaces it with a five-minute one-time schedule. Steps with triggerAutomation are forced to false by the server, so specify it directly only on the first step without a trigger |
step | Include it at both the top level and in details. It is the hop position within the flow |
sourceItem | Include both hash (path token) and filePath (plain-text path) |
syncType | Put it inside transferOptions. 1 is one-way and 2 is two-way |
Registration succeeds even if all four items are omitted, but behavior changes at execution time. If a recurring schedule was registered but ran only once, check isUpcoming first.
Progress Statistics
Query progress by transfer and synchronization run
def progress(transfers):
for folder, monitor_id in transfers.items():
detail = api("GET", f"/api/transfers/{monitor_id}")
print(f"{folder:28} {detail.get('statusName'):12}"
f" {detail.get('percent', 0):>5}%"
f" {detail.get('fileCount', 0)} files")Review each run's synchronization-automation results in the execution history.
for folder, automation_id in syncs.items():
runs = api("GET", f"/api/automations/{automation_id}/executions") or []
latest = runs[0] if runs else {}
print(f"{folder:28} runs {len(runs):>3}"
f" last {latest.get('startTime')} {latest.get('status')}")The latest execution appears first in the array. If the history is empty, the automation was registered but not executed, so check isUpcoming and the start condition.
Cutover Verification
Verify that source and cloud files match
To determine the cutover point, verify that files in the two environments match. Compare the source and target file lists by folder to find missing files.
def list_names(device_id, path):
names, page = set(), 1
while True:
result = api("GET", f"/api/devices/{device_id}/files", params={
"path": path, "page": page, "size": 200, "type": "file",
}) or {}
names.update(item["name"] for item in result.get("items") or [])
if page >= (result.get("lastPage") or 1):
return names
page += 1
missing = list_names("device-nas-01", "/share/team/design") \
- list_names("device-cloud-01", "/migrated/team/design")
print(f"{len(missing)} missing")If files are missing, retransmit the corresponding folder to align the lists. To compare size as well as names, include size in the list.
Cutover and Cleanup
Stop synchronization and make the cloud the baseline environment
After the cutover, stop the synchronization automation. If the source continues to be applied, changes on the cloud side may be overwritten.
for automation_id in syncs.values():
api("POST", f"/api/automations/{automation_id}/pause", {"pause": True})Delete it after confirming there are no problems for a period of time. Leaving it paused may allow it to be accidentally resumed later.
for automation_id in syncs.values():
api("DELETE", f"/api/automations/{automation_id}")| Check Item | Details |
|---|---|
| Migration Scope | File count and total capacity |
| Initial Migration | Transfer status by folder |
| Change Synchronization | Execution results by run |
| Verification | Whether source and target file lists match |
| Cutover | Stop and clean up synchronization |