Provide Customer-Specific File Workspaces

TeamsIT EngineersDevelopers

Getting Started

Basic Concept

Manage File Transfers Separately by Workspace

A file workspace is a way to organize the files, storage locations, transfer targets, and access scope used for a specific task into a single space.

For example, files used by development, content, and data analysis teams can be organized into separate workspaces, with the required storage locations and transfer devices connected to each workspace.

text
File Workspace
      │
 ┌────┼───────────────┐
 ▼    ▼               ▼
Files Storage       Access
 │       │             │
 ▼       ▼             ▼
Transfer Targets   Users / Devices

This configuration lets you separate and manage files and transfer environments by workspace across multiple business tasks.

Workspace Flow

Continue from File Storage to Transfer and Result Verification

Users select a workspace with configured access permissions and store or check the required files.

When a transfer job connected to the workspace is run, the selected files are transferred to the specified devices and business environments, and the execution result can also be checked in the same workspace.

text
Select Workspace
      │
      ▼
Store · Check Files
      │
      ▼
Run Transfer Job
      │
      ▼
Reflect on Target Device
      │
      ▼
Check Processing Result

Separation Benefits

Manage Files from Multiple Tasks and Devices by Purpose

Organizing workspaces by task lets you manage files, storage locations, transfer targets, and access scope under a single management standard.

CategoryIndividual ManagementWorkspace Management
FilesCheck files in multiple locations individuallyManage task-specific files by workspace
Storage LocationCheck the location for each fileConfigure storage locations by workspace
Transfer TargetCheck the target for each jobConnect transfer devices and paths by workspace
Access ScopeCheck files by userManage access scope by workspace
Result VerificationCheck results by jobCheck execution results by workspace

Business Team

Manage and Use Required Files in Workspaces

Workspace Selection

Check Task-Specific Files and Work in One Place

When starting a task, select the workspace containing the required files and jobs.

A workspace contains the files used for the task together with connected transfer jobs, allowing the responsible user to perform file operations in the workspace required for the current task.

text
My Workspaces
     │
     ├── Project A
     │      ├── Files
     │      └── Transfers
     │
     ├── Media Team
     │      ├── Files
     │      └── Transfers
     │
     └── Data Analysis
            ├── Files
            └── Transfers

File Usage

Store Required Files and Use Them in the Next Task

Store the files required for the task in the workspace and check the files to use for the current work.

Stored files can be used with the business flow connected to the workspace, allowing the responsible user to select the required files and continue to the next task.

text
Workspace
    │
    ├── Upload Files
    │
    ├── Browse Files
    │
    └── Select Files
            │
            ▼
        Next Work

Run Transfer

Send Files Using a Prepared Transfer Job

After selecting the required files, run the transfer job configured in the workspace.

The responsible user can select the files to use for the current task and the job to run, then check progress and processing results.

text
Select Files
      │
      ▼
Select Transfer
      │
      ▼
Run
      │
 ┌────┴────┐
 ▼         ▼
Progress   Target
 │         │
 └────┬────┘
      ▼
   Result

Business teams can manage required files in workspaces and use prepared transfer flows to continue to the next task.

IT Engineer

Configure File Workspaces and Transfer Environments and Manage Them Centrally

Workspace Setup

Configure File Storage Locations and Management Scope by Task

Create workspaces according to business purposes and configure the file storage locations and management scope used by each workspace.

For example, development outputs, media files, and data processing results can each be organized into separate workspaces.

text
Organization
      │
 ┌────┼───────────────┐
 ▼    ▼               ▼
Dev  Media           Data
 │     │               │
 ▼     ▼               ▼
Storage Storage      Storage

Connect Workspaces, Target Devices, and File Paths

Connect the transfer target devices and file paths used by each workspace.

Connect servers, storage, and business devices to workspaces and configure file transfer paths to build task-specific transfer environments.

text
Workspace
     │
     ├── Source Storage
     │
     ├── Transfer Flow
     │
     └── Target Devices
             │
      ┌──────┼──────┐
      ▼      ▼      ▼
   Server  Storage  System

Access Policy

Manage Access Scope and Transfer Rules by User and Device

For each workspace, configure user and group access scope, connectable devices, file paths, and transfer jobs that can be run.

When the business environment or user configuration changes, adjust access scope and processing rules from the same policy screen.

text
Workspace Policy
       │
 ┌─────┼────────────┐
 ▼     ▼            ▼
Users Groups      Devices
 │       │            │
 ▼       ▼            ▼
Access  Operations  Paths
             │
             ▼
       Transfer Rules

Management ItemConfiguration
WorkspaceWorkspaces by user and task
Users / GroupsUsers and groups that use the workspace
DevicesConnectable devices
PathsPaths used for files
OperationsFile operations that can be run
Transfer RulesFile transfer criteria and processing rules

Operations Management

Check Transfer Results by Workspace and Overall Operational Status

Check file transfers and processing results for each workspace and centrally manage the status of multiple workspaces and connected devices.

Operators can check the execution status of a specific workspace or understand the operational situation based on job status across the entire environment.

text
Central Management
        │
 ┌──────┼───────────────┐
 ▼      ▼               ▼
Workspace Devices      Runs
 │        │              │
 └────────┼──────────────┘
          ▼
     Operations View

Workspaces can be configured and managed through the following flow.

text
Configure Workspace
       │
       ▼
Connect Storage Location
       │
       ▼
Set Target Devices · Transfer Paths
       │
       ▼
Configure User · Device Access Policies
       │
       ▼
Run Transfer Job
       │
       ▼
Check Workspace-Specific · Overall Operational Status

Developer

Include the Workspace Identifier and Token in Requests and Handle Permission Responses

Authentication Handling

Issue Tokens and Refresh Them When They Expire

The workspace API uses an access token for authentication. If no token is available, log in with the account to obtain one.

import os
import threading

import requests

BASE_URL = os.getenv("INNORIX_BASE_URL", "https://app.innorix.com").rstrip("/")


def login(email, password):
    response = requests.post(f"{BASE_URL}/api/auth/login",
                             json={"email": email, "password": password},
                             timeout=30)
    response.raise_for_status()

    # the login response nests the tokens under data.user
    user = response.json()["data"]["user"]
    return user["accessToken"], user.get("refreshToken")


def refresh(refresh_token):
    response = requests.post(f"{BASE_URL}/api/auth/token/refresh",
                             headers={"X-Refresh-Token": refresh_token},
                             timeout=30)
    response.raise_for_status()

    # the refresh response nests the tokens directly under data
    data = response.json()["data"]
    return data["accessToken"], data.get("refreshToken")

The response structures for login and refresh are different. Login nests the tokens under data.user, while refresh places them directly under data.

A refresh token is valid only once. If a server application uses the same token across multiple threads, lock the refresh operation so it cannot occur concurrently.

class Session:
    def __init__(self, email, password):
        self._lock = threading.Lock()
        self._access, self._refresh = login(email, password)

    @property
    def access_token(self):
        with self._lock:
            return self._access

    def renew(self):
        with self._lock:
            self._access, self._refresh = refresh(self._refresh)
            return self._access

Workspace Assignment

Send the Workspace Identifier with Every Request

The workspace is passed in a header rather than the request body. Because specifying it at each call site can lead to omissions, centralize request construction in one place.

class Client:
    def __init__(self, session, workspace_id=None):
        self.session = session
        self.workspace_id = workspace_id

    def request(self, method, path, body=None, params=None, retried=False):
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.session.access_token}",
        }

        # when omitted the account's current workspace is used
        if self.workspace_id:
            headers["x-workspace-id"] = self.workspace_id

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

        if response.status_code == 401 and not retried:
            self.session.renew()
            return self.request(method, path, body, params, retried=True)

        return response

If no workspace identifier is specified, the account's currently accessible workspace is used. An application that uses a single workspace can operate without specifying one.

Token expiration is also handled at this point. When a 401 is received, refresh once and retry; if the retried request fails again, propagate the failure as-is.

Check Access Scope

Check the Account's View Scope and Accessible Devices

me = api("GET", "/api/auth/me") or {}
print(me)

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

if result.get("viewScope"):
    print("view scope:", result["viewScope"])

for device in result.get("devices") or []:
    print(device["deviceId"], device["name"])
Response ItemDetails
devicesList of devices connected to the workspace
totalRows · lastPageTotal count and last page
viewScopeCurrent account view scope

The device list is returned as the data.devices array. Use viewScope in the response to determine whether the account can view everything or only part of the environment.

Permission Handling

Distinguish 401 and 403 and Handle Them with Re-login or UI Guidance

Authentication failure and insufficient permissions must be handled differently. The former requires re-login, while the latter requires UI guidance.

StatusMeaningHandling
401Token is missing or invalidRefresh and retry; if it fails, re-login
403Cannot access the workspace or pathInform the user
404Device or path does not existInform the user that the target is no longer available
429Too many requestsRetry after a short delay
class WorkspaceForbidden(Exception):
    pass


def call(client, method, path, body=None, params=None):
    response = client.request(method, path, body, params)

    if response.status_code == 403:
        raise WorkspaceForbidden(path)

    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")


try:
    devices = call(client, "GET", "/api/devices")
except WorkspaceForbidden:
    devices, notice = [], "no permission to access this workspace"

Separating insufficient permissions into a dedicated exception lets the calling code convert them into a user-facing message. Because results can differ by workspace even for the same user, display the current workspace at the top of the UI so users do not mistake an empty list for a permission problem.

Multi-Workspace Handling

Separate Credentials in Code That Handles Multiple Workspaces

Passing the workspace identifier as a function argument can lead to omissions at some call site. Create a client for each workspace and pass the client object instead.

clients = {
    workspace_id: Client(session, workspace_id)
    for workspace_id in WORKSPACE_IDS
}


def collect_devices():
    result = {}

    for workspace_id, client in clients.items():
        try:
            result[workspace_id] = call(client, "GET", "/api/devices")
        except WorkspaceForbidden:
            continue

    return result

There is no endpoint for listing workspaces. Provide candidate workspaces and filter them to determine which are accessible.

If each customer has a separate account, separate the sessions as well. Keep their creation points separate so they are not mixed with a configuration where multiple workspaces share one session.

def build_client(config):
    session = Session(config["email"], config["password"])
    return Client(session, config["workspace_id"])


clients = {name: build_client(cfg) for name, cfg in TENANTS.items()}

A batch that iterates over multiple workspaces should not let a failure in one workspace stop the entire batch.

failed = []

for name, client in clients.items():
    try:
        run_for(client)
    except Exception as error:
        failed.append((name, error))

for name, error in failed:
    print(f"{name}: {error}")
Management ItemHandled in Code
SessionAccess and refresh tokens by account
WorkspaceWorkspace identifier in the request header
Permissions403 response and UI guidance
ResourcesDevices and view scope by workspace
ExecutionTransfers created within the workspace