Good morning, I’d like to share a small Node.js script that bulk-closes all the error tasks in a workflow. This saves you from having to open each individual task, close it, and confirm manually. Hope it comes in handy. Have a great day!
const axios = require("axios");
const { wrapper } = require("axios-cookiejar-support");
const { CookieJar } = require("tough-cookie");
const qs = require("qs");
const DOCUWARE_BASE = "https://yourdocuwareurl/DocuWare/Platform";
const USERNAME = "whatever";
const PASSWORD = "whatever";
const HOST_ID = "5e2aff28-c837-4484-acb8-e13fabca410c"; //just any valid guid
const workflowGuid= "7aedfb48-8ae5-4f41-aadd-c07724c93d77"; //workflow guid
axios.defaults.timeout = 30000; // aumenta la tolleranza
const jar = new CookieJar();
const client = wrapper(
axios.create({
baseURL: DOCUWARE_BASE,
jar,
withCredentials: true,
headers: {
"Accept": "application/json"
}
})
);
// ------------------------------------------------------------
// RETRY AUTOMATICO con attesa progressiva
// ------------------------------------------------------------
async function safeRequest(requestFn, maxRetries = 5, delayMs = 2000) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await requestFn();
} catch (err) {
const code = err.code;
const status = err.response?.status;
const isNetworkError =
code === "ETIMEDOUT" ||
code === "ECONNRESET" ||
code === "EAI_AGAIN";
const isServerError =
status === 500 ||
status === 502 ||
status === 503 ||
status === 504;
if (isNetworkError || isServerError) {
console.log(
`⚠️ attempt ${attempt}/${maxRetries} failed (${code || status}).`
);
console.log(`⏳ wait ${delayMs}ms and retry...`);
await new Promise((resolve) => setTimeout(resolve, delayMs));
delayMs = Math.floor(delayMs * 1.5);
continue;
}
throw err;
}
}
throw new Error("❌ Too many attempts failed.");
}
// ------------------------------------------------------------
// LOGIN
// ------------------------------------------------------------
async function docuwareLogin() {
const formBody = qs.stringify({
UserName: USERNAME,
Password: PASSWORD,
Organization: "",
RedirectToMyselfInCaseOfError: false,
RememberMe: false,
HostID: HOST_ID,
LicenseType: "PlatformService"
});
try {
await safeRequest(() =>
client.post("/Account/Logon", formBody, {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json"
}
})
);
console.log("🔐 Login OK");
} catch (err) {
console.log("❌ Login failed:", err.response?.data || err.message);
process.exit(1);
}
}
// ------------------------------------------------------------
// LOGOUT
// ------------------------------------------------------------
async function logout() {
try {
await safeRequest(() => client.get("/Account/Logoff"));
console.log("🚪 Logout OK");
} catch (err) {
console.log("⚠️ Logout failed:", err.response?.data || err.message);
}
}
// ------------------------------------------------------------
// get 200 TASK
// ------------------------------------------------------------
async function getNextTasksPage(url) {
const res = await safeRequest(() => client.get(url));
const tasks = res.data.Task || [];
const nextLink = res.data.Links?.find((l) => l.rel === "next");
let nextUrl = null;
if (nextLink) {
nextUrl = nextLink.href
.replace("/DocuWare/Platform", "")
.replace("count=50", "count=200");
}
return { tasks, nextUrl };
}
// ------------------------------------------------------------
// PROCESS TASK
// ------------------------------------------------------------
async function processTask(task, workflowGuid) {
console.log(`\n➡️ Task: ${task.Id} — ${task.DisplayName}`);
const instanceRes = await safeRequest(() =>
client.get(
`/Workflow/Workflows/${workflowGuid}/Instances/${task.InstanceId}/Tasks/${task.Id}`
)
);
//console.log(" Stato:", instanceRes.data.State);
console.log(" ⛔ close task…");
let linkCloseAction =
instanceRes.data.Decisions[2].DecisionOperations.BaseDecisionOperations
.Links[0].href;
linkCloseAction = linkCloseAction.replace("/DocuWare/Platform", "");
const confirmscreen = await safeRequest(() => client.get(linkCloseAction));
let confirmAction =
confirmscreen.data.DecisionOperations.ExtendedDecisionOperations.Links[0]
.href;
confirmAction = confirmAction.replace("/DocuWare/Platform", "");
const jsonBody = { ConfirmedFields: [] };
await safeRequest(() =>
client.post(confirmAction, jsonBody, {
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
})
);
console.log(" ✅ Task chiusa");
}
async function processAllTasks(workflowGuid) {
let nextUrl = `/Workflow/Workflows/${workflowGuid}/Tasks?count=200`;
let page = 1;
while (nextUrl) {
console.log(`\n📦 Pagina ${page}: load tasks block…`);
const { tasks, nextUrl: newNextUrl } = await getNextTasksPage(nextUrl);
console.log(`➡️ ${tasks.length} task trovate`);
for (const task of tasks) {
await processTask(task, workflowGuid);
}
nextUrl = newNextUrl;
page++;
}
console.log("\n🎉 Task completed!");
}
// ------------------------------------------------------------
// MAIN
// ------------------------------------------------------------
async function main() {
await docuwareLogin();
try {
await processAllTasks(workflowGuid);
} catch (err) {
console.error("\n❌ ERRORE GENERALE:", err.response?.data || err.message);
} finally {
await logout();
}
}
main();
Hi,
I find very poor that there is no way to use a form in the steps of a workflow and user has to fill the information in the decision area that give very limited possibility to show information , rich fields etc. but hopefully this is something that will come soon with a new version.
In the while I would like to use external customer web page as a workaround, at least for some activity where the answer require full screen , list of values and a nice form to fill relying on the docuware workflow engine.
My idea is to have a link in the decision area that call my page so the user open the external link , fill the form and via api I can interact with Docuware decision send data and even save document .
What I need in my external link is the Instance ID of the workflow which I cannot see as an available System Field in the workflow designer, am I missing something ?
How can I know , without looping on all the active instances , what is the workflow and activity the user has to work on ?
I can assign via api a unique custom GuiId to the workflow and I can use this as a key field for the workflow, but is there any way to query the workflow engine via api and filter based on the value of a variable to get the instance reference ?
Thanks
Alberto