Skip to main content

Detect "confidential" documents and move them to a secure room

This example shows how to automatically detect sensitive documents in ONLYOFFICE DocSpace by searching for the keyword confidential and moving such files into a secure folder (for example, inside a secure room).

The script:

  • lists all items inside a starting room/folder (including subfolders)
  • keeps files only
  • checks file titles against KEYWORDS
  • moves matched files into a predefined secure folder
  • can be run on demand or scheduled (cron/task scheduler)

There are no webhooks in this case - it is a batch scan that you can start whenever needed.

Before you start

  1. Replace https://yourportal.onlyoffice.com and YOUR_API_KEY with your actual DocSpace portal URL and API key. Ensure you have the necessary data and permissions to perform these operations.
  2. Before you can make requests to the API, you need to authenticate. Check out the Personal access tokens page to learn how to obtain and use access tokens.
Full example
// Config
const API_HOST = process.env.DOCSPACE_API_HOST; // Set DOCSPACE_API_HOST in env (recommended). For quick tests you can temporarily paste your portal URL here.
const API_KEY = process.env.DOCSPACE_API_KEY; // Set DOCSPACE_API_KEY in env (recommended). For quick tests you can temporarily paste token here.

// Scan configuration
const START_FOLDER_ID = 539564; // roomId or folderId to scan (replace)
const SECURE_FOLDER_ID = 748239; // secure destination folderId (replace)

const KEYWORDS = ["confidential", "secret", "internal only", "sensitive"];

const HEADERS: Record<string, string> = {
Accept: "application/json",
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
};

// Request helper
async function docspaceRequest(path: string, method: string = "GET", body: any = null) {
const url = `${API_HOST}${path}`;

try {
const res = await fetch(url, {
method,
headers: HEADERS,
body: body ? JSON.stringify(body) : undefined,
});

if (!res.ok) {
const text = await res.text().catch(() => "");
console.error(`[ERROR] Request failed: ${method} ${url}`);
console.error(`[ERROR] Status: ${res.status}, Message: ${text}`);
return null;
}

return await res.json();
} catch (err: any) {
console.error(`[ERROR] Request error: ${err?.message || err}`);
return null;
}
}

function getResponseNode(data: any) {
if (data && typeof data === "object" && "response" in data) return (data as any).response;
return data;
}

function titleMatchesKeywords(title: string) {
const t = String(title || "").toLowerCase();
return KEYWORDS.some((kw) => t.includes(String(kw).toLowerCase()));
}

// Step 1: List folder contents (files + folders)
async function listFolderContents(folderId: number) {
const data = await docspaceRequest(`/api/2.0/files/${folderId}`, "GET");
const node = getResponseNode(data);

if (!node || typeof node !== "object") {
return { files: [], folders: [] };
}

const files = Array.isArray((node as any).files) ? (node as any).files : [];
const folders = Array.isArray((node as any).folders) ? (node as any).folders : [];

return { files, folders };
}

// Step 2: Recursively collect matching files by title
async function collectSensitiveFiles(startFolderId: number) {
const result: Array<{ id: number; title: string }> = [];

async function walk(folderId: number) {
console.log(`[INFO] Scanning folder/room ${folderId}...`);
const { files, folders } = await listFolderContents(folderId);

for (const f of files) {
const fileId = Number((f as any).id);
const title = String((f as any).title || "");
if (!Number.isFinite(fileId) || !title) continue;

if (titleMatchesKeywords(title)) {
console.log(` [MATCH] fileId=${fileId}, title="${title}"`);
result.push({ id: fileId, title });
}
}

for (const sub of folders) {
const subId = Number((sub as any).id);
if (!Number.isFinite(subId)) continue;
await walk(subId);
}
}

await walk(startFolderId);
return result;
}

// Step 3: Move files to secure folder
async function moveFilesToSecureFolder(fileIds: number[]) {
if (!fileIds.length) return true;

const payload = {
fileIds,
destFolderId: SECURE_FOLDER_ID,
deleteAfter: true,
content: true,
toFillOut: false,
};

const data = await docspaceRequest("/api/2.0/files/fileops/move", "PUT", payload);
return Boolean(data);
}

// Run flow
(async () => {
console.log("[INFO] Scanning DocSpace for sensitive documents by TITLE...");
console.log(`[INFO] Start folder/room: ${START_FOLDER_ID}`);
console.log(`[INFO] Keywords: ${JSON.stringify(KEYWORDS)}`);
console.log(`[INFO] Secure folder: ${SECURE_FOLDER_ID}\n`);

const matches = await collectSensitiveFiles(START_FOLDER_ID);

if (!matches.length) {
console.log("\n[INFO] No matching files were found.");
return;
}

console.log(`\n[INFO] Found ${matches.length} file(s). Moving...`);

// Optional: move in chunks if you expect large batches
const ids = matches.map((x) => x.id);
const ok = await moveFilesToSecureFolder(ids);

if (ok) console.log(`[INFO] Done. Moved ${ids.length} file(s) to secure folder ${SECURE_FOLDER_ID}.`);
else console.error("[ERROR] Move failed.");

console.log("\n[INFO] Sensitive document scan completed.");
})().catch((e) => {
console.error("[ERROR] Scan failed:", e?.message || e);
process.exitCode = 1;
});

Step 1: List files in the target room (including subfolders)

The script starts from START_FOLDER_ID (a room ID or folder ID) and reads its contents using GET /api/2.0/files/:folderId.

DocSpace returns two lists:

files — files inside this folder folders — subfolders inside this folder

The script repeats the same request for every subfolder to scan the whole tree.

async function listFolderContents(folderId: number) {
const data = await docspaceRequest(`/api/2.0/files/${folderId}`, "GET");
const node = getResponseNode(data);

if (!node || typeof node !== "object") {
return { files: [], folders: [] };
}

const files = Array.isArray(node.files) ? node.files : [];
const folders = Array.isArray(node.folders) ? node.folders : [];

return { files, folders };
}

Step 2: Detect sensitive files by title

For every file found during the scan, the script checks the title field against KEYWORDS (case-insensitive). Only files are evaluated — folders are scanned only to reach deeper levels.

If a file title contains any keyword (for example, confidential), the file is added to the move list.

async function collectSensitiveFiles(startFolderId: number) {
const result: Array<{ id: number; title: string }> = [];

async function walk(folderId: number) {
const { files, folders } = await listFolderContents(folderId);

for (const f of files) {
const fileId = Number(f.id);
const title = String(f.title || "");
if (!Number.isFinite(fileId) || !title) continue;

if (titleMatchesKeywords(title)) result.push({ id: fileId, title });
}

for (const sub of folders) {
const subId = Number(sub.id);
if (!Number.isFinite(subId)) continue;
await walk(subId);
}
}

await walk(startFolderId);
return result;
}

Step 3: Move matched files to a secure folder

If at least one file matches, the script moves them into SECURE_FOLDER_ID using PUT /api/2.0/files/fileops/move with body:

{ "fileIds": [...], "destFolderId": SECURE_FOLDER_ID, "deleteAfter": true }

This relocates sensitive files into a controlled secure location for review.

async function moveFilesToSecureFolder(fileIds: number[]) {
const payload = {
fileIds,
destFolderId: SECURE_FOLDER_ID,
deleteAfter: true,
content: true,
toFillOut: false,
};

const data = await docspaceRequest("/api/2.0/files/fileops/move", "PUT", payload);
return Boolean(data);
}