Skip to main content

Create a project room from a template

This example demonstrates how to create a DocSpace room for a new project from an existing room template. The external project system (PM/ERP/CRM) sends project data (for example, projectId, projectType, projectName). The integration then:

  • fetches available room templates,
  • selects a template based on projectType,
  • starts room creation from the selected template,
  • waits until the room is created,
  • returns the created roomId (and optionally room info) back to the external system.

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.

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

const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

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

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

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

return res.json();
}

// Step 1: Load available templates and choose one for the project
async function selectTemplateForProject(projectType: string) {
const data = await docspaceRequest("/api/2.0/files/rooms?searchArea=Templates", "GET");
const resp = data?.response ?? null;

const templates = Array.isArray(resp?.folders) ? resp.folders : [];
if (!templates.length) {
console.log("No templates found in Templates.");
return null;
}

// Example mapping: projectType -> template title contains keyword
const normalized = String(projectType || "").toLowerCase();
const match = templates.find((t: any) => String(t?.title || "").toLowerCase().includes(normalized));

// Fallback: first template
const chosen = match || templates[0];

const templateId = Number(chosen?.id);
if (!Number.isFinite(templateId)) return null;

console.log(`Template selected: "${chosen.title}" (id=${templateId})`);
return { templateId, templateTitle: String(chosen.title || "") };
}

// Step 2: Start creating a room from the selected template
async function startRoomFromTemplate(templateId: number, roomTitle: string) {
// The docs describe POST /api/2.0/files/rooms/fromtemplate
// The exact body fields can vary by build; keep it minimal and aligned to “template + title”.
const payload = {
templateId,
title: roomTitle,
};

const data = await docspaceRequest("/api/2.0/files/rooms/fromtemplate", "POST", payload);
if (!data) return null;

// Different builds may return different “task/operation” identifiers.
// We keep it tolerant and store the full response for Step 3.
console.log("Room creation started.");
return data;
}

// Step 3: Wait until the room is created (poll creation status)
async function waitForRoomCreation() {
// Docs: GET /api/2.0/files/rooms/fromtemplate/status
// We poll until API reports a completed task and provides a roomId (or similar).
for (let i = 0; i < 30; i++) {
const data = await docspaceRequest("/api/2.0/files/rooms/fromtemplate/status", "GET");
const resp = data?.response ?? null;

// Common patterns: response can be an object or a list of tasks.
const tasks = Array.isArray(resp) ? resp : Array.isArray(resp?.tasks) ? resp.tasks : [];

const completed = tasks.find((t: any) => Boolean(t?.isCompleted || t?.completed || t?.status === "Completed"));
if (completed) {
const roomId = Number(completed?.roomId ?? completed?.id ?? null);
if (Number.isFinite(roomId)) {
console.log(`Room creation completed: roomId=${roomId}`);
return roomId;
}
}

console.log("Room is still being created...");
await sleep(2000);
}

console.log("Timed out waiting for room creation status.");
return null;
}

// Step 4: Load room info (optional, but useful to return metadata to external system)
async function getRoomInfo(roomId: number) {
const data = await docspaceRequest(`/api/2.0/files/rooms/${roomId}`, "GET");
const room = data?.response ?? null;
return room && typeof room === "object" ? room : null;
}

// Step 5: Return results to the external project system (placeholder)
async function updateExternalProject(projectId: string, roomId: number, roomInfo: any) {
// Replace this with your PM system API call.
console.log("[EXTERNAL SYSTEM UPDATE]");
console.log(`projectId: ${projectId}`);
console.log(`roomId: ${roomId}`);
console.log("roomInfo:", roomInfo);
}

// Run flow (example input from PM webhook)
(async () => {
const project = {
projectId: "PRJ-10021",
projectType: "marketing",
projectName: "Q1 Campaign",
};

// Step 1: Select a template for the project type
const template = await selectTemplateForProject(project.projectType);
if (!template) return;

// Step 2: Start room creation from the chosen template
const roomTitle = `${project.projectName} (${project.projectId})`;
const started = await startRoomFromTemplate(template.templateId, roomTitle);
if (!started) return;

// Step 3: Wait until the room is created and get its ID
const roomId = await waitForRoomCreation();
if (!roomId) return;

// Step 4: Load room details
const roomInfo = await getRoomInfo(roomId);

// Step 5: Update external project system (placeholder)
await updateExternalProject(project.projectId, roomId, roomInfo);
})();

Step 1: Get templates and choose one for the project

The integration loads templates from DocSpace and picks a suitable template for the project type.

It requests the templates list using GET /api/2.0/files/rooms?searchArea=Templates and selects a template (for example, by matching projectType against template title).

async function selectTemplateForProject(projectType: string) {
const data = await docspaceRequest("/api/2.0/files/rooms?searchArea=Templates", "GET");
const templates = Array.isArray(data?.response?.folders) ? data.response.folders : [];

const normalized = String(projectType || "").toLowerCase();
const match = templates.find((t: any) => String(t?.title || "").toLowerCase().includes(normalized));
const chosen = match || templates[0];

const templateId = Number(chosen?.id);
if (!Number.isFinite(templateId)) return null;

return { templateId, templateTitle: String(chosen.title || "") };
}

Step 2: Start room creation from template

Once the template is selected, the integration starts room creation using POST /api/2.0/files/rooms/fromtemplate

In this example the minimal payload is { "templateId": <templateId>, "title": "<projectRoomTitle>" }

async function startRoomFromTemplate(templateId: number, roomTitle: string) {
const payload = { templateId, title: roomTitle };
return await docspaceRequest("/api/2.0/files/rooms/fromtemplate", "POST", payload);
}

Step 3: Wait until the room is created

Room creation from a template may take time (copying structure, files, permissions). The integration periodically checks the creation progress using GET /api/2.0/files/rooms/fromtemplate/status. When a completed task appears and a roomId becomes available, the integration continues.

async function waitForRoomCreation() {
for (let i = 0; i < 30; i++) {
const data = await docspaceRequest("/api/2.0/files/rooms/fromtemplate/status", "GET");
const resp = data?.response ?? null;

const tasks = Array.isArray(resp) ? resp : Array.isArray(resp?.tasks) ? resp.tasks : [];
const completed = tasks.find((t: any) => Boolean(t?.isCompleted || t?.completed || t?.status === "Completed"));

if (completed) {
const roomId = Number(completed?.roomId ?? completed?.id ?? null);
if (Number.isFinite(roomId)) return roomId;
}

await sleep(2000);
}
return null;
}

Step 4: Load room info (optional)

After the room is created, you may want to load its metadata (title, flags, ids) before sending it back to the external system.

The integration requests room information using GET /api/2.0/files/rooms/:roomId (room info endpoint is available in the rooms section of the API docs).

async function getRoomInfo(roomId: number) {
const data = await docspaceRequest(`/api/2.0/files/rooms/${roomId}`, "GET");
return data?.response ?? null;
}

Step 5: Save the room back to the external project system

Finally, the integration returns the created room identifier to the external project system (PM/ERP/CRM) so the project card can store:

  • roomId
  • optional room metadata (title, etc.)

In production this step is usually implemented as an API call to the external system (custom field update). In this example it is shown as a placeholder method.

async function updateExternalProject(projectId: string, roomId: number, roomInfo: any) {
console.log("[EXTERNAL SYSTEM UPDATE]");
console.log({ projectId, roomId, roomInfo });
}