Skip to main content

Build a weekly report of contracts in recently created rooms

This example demonstrates how to generate a weekly contracts report for rooms created recently in ONLYOFFICE DocSpace. The script loads rooms, filters them by creation date, scans files inside each room, detects a contract file by keywords, and marks it as SIGNED or NOT SIGNED based on the file name (placeholder logic).

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
// Set API base URL
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.

// How many days back to scan rooms
const DAYS = 7;

// Keywords to detect a contract file
const CONTRACT_KEYWORDS = ['contract', 'agreement'];

// Keywords to detect a signed contract (by file name)
const SIGNED_KEYWORDS = ['signed', 'signed_'];

const HEADERS = {
Accept: 'application/json',
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
};

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();
console.log(`Request failed: ${method} ${url}`);
console.log(`Status: ${res.status}, Message: ${text}`);
return null;
}

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

function toUtcIso(d: Date) {
return d.toISOString();
}

function parseRoomCreated(room: any) {
const created = room?.created;

let raw: string | null = null;

if (created && typeof created === 'object') {
raw = created.on || created.date || created.utc || created.time || null;
} else if (typeof created === 'string') {
raw = created;
}

if (!raw) return null;

const normalized = raw.includes('Z') ? raw.replace('Z', '+00:00') : raw;
const dt = new Date(normalized);

if (Number.isNaN(dt.getTime())) return null;
return dt;
}

// Step 1: Load rooms created during the selected period
async function getRooms() {
const data = await docspaceRequest('/api/2.0/files/rooms?count=100&startIndex=0', 'GET');
if (!data || typeof data !== 'object') return [];

const resp = data.response != null ? data.response : data;
if (!resp || typeof resp !== 'object') return [];

const rooms = (resp as any).folders;
return Array.isArray(rooms) ? rooms : [];
}

// Step 2: Scan files inside each room and find a contract
async function getRoomFiles(roomId: number) {
const data = await docspaceRequest(`/api/2.0/files/${roomId}`, 'GET');
if (!data || typeof data !== 'object') return [];

const resp = data.response != null ? data.response : data;
if (!resp || typeof resp !== 'object') return [];

const files = (resp as any).files;
if (!Array.isArray(files)) return [];

return files
.map((it: any) => {
const idNum = Number(it?.id ?? it?.fileId);
const title = String(it?.title || '');
if (!Number.isFinite(idNum) || !title) return null;
return { id: idNum, title };
})
.filter(Boolean);
}

function includesAny(text: string, keywords: string[]) {
const t = text.toLowerCase();
for (const kw of keywords) {
if (t.includes(String(kw).toLowerCase())) return true;
}
return false;
}

function isSignedByTitle(title: string) {
return includesAny(title, SIGNED_KEYWORDS);
}

// Step 3: Detect whether the contract is signed and print the report
async function buildWeeklyContractsReport() {
const now = new Date();
const from = new Date(now.getTime() - DAYS * 24 * 60 * 60 * 1000);

console.log(`Checking rooms from ${toUtcIso(from)} to ${toUtcIso(now)}...`);

const rooms = await getRooms();
if (!rooms.length) {
console.log('No rooms returned by API.');
return;
}

const results: any[] = [];

for (const room of rooms) {
if (!room || typeof room !== 'object') continue;

const createdDt = parseRoomCreated(room);

// If created date can't be parsed, skip to keep the report predictable.
if (!createdDt) continue;
if (createdDt < from || createdDt > now) continue;

const roomId = Number(room.id ?? room.roomId);
if (!Number.isFinite(roomId)) continue;

const roomTitle = String(room.title || room.name || `Room ${roomId}`);

const files = await getRoomFiles(roomId);
if (!files.length) continue;

const contract = files.find((f: any) => includesAny(f.title, CONTRACT_KEYWORDS));
if (!contract) continue;

results.push({
roomId,
roomTitle,
created: createdDt.toISOString(),
contractFileId: contract.id,
contractFileTitle: contract.title,
signed: isSignedByTitle(contract.title),
});
}

console.log('--- Weekly Clients Contracts Report ---');

if (!results.length) {
console.log('No contract files found in rooms created during this period.');
return;
}

for (const r of results) {
const status = r.signed ? 'SIGNED' : 'NOT SIGNED';
console.log(
`Room: ${r.roomTitle} (ID: ${r.roomId})\n` +
` Created: ${r.created}\n` +
` Contract: ${r.contractFileTitle} (File ID: ${r.contractFileId})\n` +
` Status: ${status}\n`
);
}
}

(async () => {
try {
await buildWeeklyContractsReport();
} catch (err: any) {
console.error(err?.message || err);
}
})();

Step 1: Load rooms created during the selected period

First, the script sends a GET request to /api/2.0/files/rooms and reads the room list from response.folders. Then it parses the creation timestamp for each room (for example, created.on or a similar field returned by the API) and keeps only rooms created within the last DAYS days. rooms with an unrecognized creation date are skipped to keep the report predictable.

async function getRooms() {
const data = await docspaceRequest('/api/2.0/files/rooms?count=100&startIndex=0', 'GET');
if (!data || typeof data !== 'object') return [];

const resp = data.response != null ? data.response : data;
if (!resp || typeof resp !== 'object') return [];

const rooms = (resp as any).folders;
return Array.isArray(rooms) ? rooms : [];
}

Step 2: Scan files inside each room and find a contract

For every room in the filtered list, the script sends a GET request to /api/2.0/files/:folderId (where folderId is the room root ID in this example) and reads the file list from response.files. A file is treated as a contract if its title contains any value from CONTRACT_KEYWORDS (for example: contract, agreement). The keyword matching is case-insensitive.

async function getRoomFiles(roomId: number) {
const data = await docspaceRequest(`/api/2.0/files/${roomId}`, 'GET');
if (!data || typeof data !== 'object') return [];

const resp = data.response != null ? data.response : data;
if (!resp || typeof resp !== 'object') return [];

const files = (resp as any).files;
if (!Array.isArray(files)) return [];

return files
.map((it: any) => {
const idNum = Number(it?.id ?? it?.fileId);
const title = String(it?.title || '');
if (!Number.isFinite(idNum) || !title) return null;
return { id: idNum, title };
})
.filter(Boolean);
}

Step 3: Detect whether the contract is signed and print the report

When a contract file is found, the script checks its title for SIGNED_KEYWORDS and marks the contract as:

  • SIGNED — if the file name contains any signed keyword (for example: signed, signed_);
  • NOT SIGNED — if no signed keyword is found. This is a placeholder rule based purely on file naming. In a real workflow, you can replace it with stronger logic (for example, checking a status field, metadata, approval workflow, or a dedicated “Signed” folder).
async function buildWeeklyContractsReport() {
const now = new Date();
const from = new Date(now.getTime() - DAYS * 24 * 60 * 60 * 1000);

console.log(`Checking rooms from ${toUtcIso(from)} to ${toUtcIso(now)}...`);

const rooms = await getRooms();
if (!rooms.length) {
console.log('No rooms returned by API.');
return;
}

const results: any[] = [];

for (const room of rooms) {
if (!room || typeof room !== 'object') continue;

const createdDt = parseRoomCreated(room);

// If created date can't be parsed, skip to keep the report predictable.
if (!createdDt) continue;
if (createdDt < from || createdDt > now) continue;

const roomId = Number(room.id ?? room.roomId);
if (!Number.isFinite(roomId)) continue;

const roomTitle = String(room.title || room.name || `Room ${roomId}`);

const files = await getRoomFiles(roomId);
if (!files.length) continue;

const contract = files.find((f: any) => includesAny(f.title, CONTRACT_KEYWORDS));
if (!contract) continue;

results.push({
roomId,
roomTitle,
created: createdDt.toISOString(),
contractFileId: contract.id,
contractFileTitle: contract.title,
signed: isSignedByTitle(contract.title),
});
}

console.log('--- Weekly Clients Contracts Report ---');

if (!results.length) {
console.log('No contract files found in rooms created during this period.');
return;
}

for (const r of results) {
const status = r.signed ? 'SIGNED' : 'NOT SIGNED';
console.log(
`Room: ${r.roomTitle} (ID: ${r.roomId})\n` +
` Created: ${r.created}\n` +
` Contract: ${r.contractFileTitle} (File ID: ${r.contractFileId})\n` +
` Status: ${status}\n`
);
}
}