Skip to main content

Remind inactive room members based on audit events

This example shows how to use DocSpace audit events to identify room members who are not participating. It loads audit events for the last N days, counts events per user for selected rooms, and sends a reminder to members whose activity is below a defined threshold. Optionally, it sends a short summary report to a room owner/manager.

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.

const MONITORED_ROOM_IDS = [463996, 274818];
const PERIOD_DAYS = 30;
const MIN_ACTIVITY = 1;

const MANAGER_EMAIL = 'manager@company.com';

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

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

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

if (!res.ok) {
const t = await res.text();
console.log(`DocSpace request failed. Status: ${res.status}, Message: ${t}`);
return null;
}

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

// Step 1: Get room members (placeholder)
function fetchRoomMembers(roomId: number) {
if (roomId === 463996) {
return [
{ id: 'user-aaa', displayName: 'Alice Smith', email: 'alice@example.com' },
{ id: 'user-bbb', displayName: 'Bob Johnson', email: 'bob@example.com' },
];
}

if (roomId === 274818) {
return [
{ id: 'user-ccc', displayName: 'Carol Davis', email: 'carol@example.com' },
];
}

return [];
}

// Step 2: Load audit events for the last PERIOD_DAYS
async function getAuditEvents(fromIso: string, toIso: string) {
const query = `from=${encodeURIComponent(fromIso)}&to=${encodeURIComponent(toIso)}`;
const data = await docspaceRequest(`/api/2.0/security/audit/events/filter?${query}`, 'GET');

if (!data || typeof data !== 'object') return [];
const events = (data as any).response;

return Array.isArray(events) ? events : [];
}

function getRoomIdFromEvent(evt: any) {
const direct =
evt.roomId ||
evt.spaceId ||
evt.folderId;

if (direct != null) return Number(direct);

const entity = evt.entity && typeof evt.entity === 'object' ? evt.entity : null;
if (!entity) return null;

const nested = entity.roomId || entity.spaceId;
if (nested == null) return null;

const n = Number(nested);
return Number.isFinite(n) ? n : null;
}

function isRoomActivityEvent(evt: any, roomIds: number[]) {
const entityType = String(evt.entityType || evt.targetType || evt.entity || '').toLowerCase();
const isFileOrFolder =
entityType.includes('file') ||
entityType.includes('document') ||
entityType.includes('folder') ||
entityType.includes('room');

if (!isFileOrFolder) return false;

const roomId = getRoomIdFromEvent(evt);
if (roomId == null) return false;

return roomIds.includes(roomId);
}

// Step 3: Build room -> user -> activityCount map
function buildRoomUserActivityMap(events: any[], roomIds: number[]) {
const result: Record<number, Record<string, number>> = {};

for (const evt of events) {
if (!isRoomActivityEvent(evt, roomIds)) continue;

const roomId = getRoomIdFromEvent(evt);
if (roomId == null) continue;

const userId = String(evt.userId || evt.account || '');
if (!userId) continue;

if (!result[roomId]) result[roomId] = {};
if (!result[roomId][userId]) result[roomId][userId] = 0;

result[roomId][userId] += 1;
}

return result;
}

// Step 4: Send reminders and manager report (placeholders)
function sendReminderToUser(member: any, roomId: number, periodDays: number) {
const to = member.email || member.displayName || 'user';

console.log('--- USER REMINDER ---');
console.log(`To: ${to}`);
console.log('Subject: Infrequent activity in DocSpace room');
console.log(
`Body:\nHello ${member.displayName || 'there'},\n` +
`We noticed that you had no activity in room ID ${roomId} during the last ${periodDays} days.\n` +
'If this room is still relevant to you, please review its contents.'
);
}

function sendManagerReport(lines: string[]) {
console.log('--- MANAGER REPORT ---');
console.log(`To: ${MANAGER_EMAIL}`);
console.log('Subject: Monthly report: infrequent room users');

if (!lines.length) {
console.log('Body:\nAll monitored room members were active during this period.');
return;
}

console.log('Body:');
for (const line of lines) console.log(line);
}

// Main workflow
(async () => {
try {
const now = new Date();
const from = new Date(now.getTime() - PERIOD_DAYS * 24 * 60 * 60 * 1000);

const fromIso = from.toISOString();
const toIso = now.toISOString();

console.log(`Checking infrequent users from ${fromIso} to ${toIso}...`);

const roomMembersMap: Record<number, any[]> = {};
for (const roomId of MONITORED_ROOM_IDS) {
roomMembersMap[roomId] = fetchRoomMembers(roomId);
}

const events = await getAuditEvents(fromIso, toIso);
const activityMap = buildRoomUserActivityMap(events, MONITORED_ROOM_IDS);

const managerReportLines: string[] = [];

for (const roomId of MONITORED_ROOM_IDS) {
const members = roomMembersMap[roomId] || [];
const roomActivity = activityMap[roomId] || {};

if (!members.length) continue;

console.log(`Room ${roomId}: checking ${members.length} member(s)...`);

for (const member of members) {
const userId = String(member.id || '');
const count = roomActivity[userId] || 0;

if (count < MIN_ACTIVITY) {
sendReminderToUser(member, roomId, PERIOD_DAYS);
managerReportLines.push(
`Room ${roomId}: user ${member.displayName} (${member.email || userId}) had 0 events in the last ${PERIOD_DAYS} days.`
);
}
}
}

sendManagerReport(managerReportLines);
console.log('Monthly infrequent-users check completed.');
} catch (err: any) {
console.error(err?.message || err);
}
})();

Step 1: Get room members

The script loads room members for each monitored room. In this example, fetch_room_members() is a placeholder returning static data. In a real integration, replace it with a real DocSpace endpoint for room members or sharing entries.

function fetchRoomMembers(roomId: number) {
if (roomId === 463996) {
return [
{ id: 'user-aaa', displayName: 'Alice Smith', email: 'alice@example.com' },
{ id: 'user-bbb', displayName: 'Bob Johnson', email: 'bob@example.com' },
];
}

if (roomId === 274818) {
return [
{ id: 'user-ccc', displayName: 'Carol Davis', email: 'carol@example.com' },
];
}

return [];
}

Step 2: Retrieve audit events for the period

A GET request is sent to /api/2.0/security/audit/events/filter with:

  • from: start datetime
  • to: end datetime The response returns a list of audit events (usually under response).
async function getAuditEvents(fromIso: string, toIso: string) {
const query = `from=${encodeURIComponent(fromIso)}&to=${encodeURIComponent(toIso)}`;
const data = await docspaceRequest(`/api/2.0/security/audit/events/filter?${query}`, 'GET');

if (!data || typeof data !== 'object') return [];
const events = (data as any).response;

return Array.isArray(events) ? events : [];
}

Step 3: Count activity per user in monitored rooms

The script filters audit events that belong to the monitored rooms and counts events per user. If a user has fewer than MIN_ACTIVITY events, they are treated as inactive for the period.

function buildRoomUserActivityMap(events: any[], roomIds: number[]) {
const result: Record<number, Record<string, number>> = {};

for (const evt of events) {
if (!isRoomActivityEvent(evt, roomIds)) continue;

const roomId = getRoomIdFromEvent(evt);
if (roomId == null) continue;

const userId = String(evt.userId || evt.account || '');
if (!userId) continue;

if (!result[roomId]) result[roomId] = {};
if (!result[roomId][userId]) result[roomId][userId] = 0;

result[roomId][userId] += 1;
}

return result;
}

Step 4: Send reminders and a manager report

The script sends:

  • a reminder to each inactive user (placeholder)
  • one summary report to the manager (placeholder)
function sendReminderToUser(member: any, roomId: number, periodDays: number) {
const to = member.email || member.displayName || 'user';

console.log('--- USER REMINDER ---');
console.log(`To: ${to}`);
console.log('Subject: Infrequent activity in DocSpace room');
console.log(
`Body:\nHello ${member.displayName || 'there'},\n` +
`We noticed that you had no activity in room ID ${roomId} during the last ${periodDays} days.\n` +
'If this room is still relevant to you, please review its contents.'
);
}