GetWorkflowStatistics API¶
Returns comprehensive statistics for a specific workflow definition, including pending, completed, and overdue flow counts, with optional date range filtering.
Endpoint¶
Methods¶
- GET
/srv.asmx/GetWorkflowStatistics?authenticationTicket=...&domainName=...&workflowName=...&startDate=...&endDate=... - POST
/srv.asmx/GetWorkflowStatistics(form data) - SOAP Action:
http://tempuri.org/GetWorkflowStatistics
Parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
authenticationTicket |
string | Yes | Authentication ticket obtained from AuthenticateUser |
domainName |
string | Yes | Name of the domain/library containing the workflow |
workflowName |
string | Yes | Name of the workflow definition |
startDate |
DateTime | No | Start date for range filtering (null for all-time only) |
endDate |
DateTime | No | End date for range filtering (null for all-time only) |
Response Behavior¶
All-Time Statistics Only (No Date Range)¶
When startDate and endDate are not provided or null:
GET /srv.asmx/GetWorkflowStatistics?authenticationTicket=xxx&domainName=Engineering&workflowName=DocumentReview
Response includes: - Domain and workflow information - Planned length of the workflow (hours and text) - Total pending count (all-time) - Total completed count (all-time) - Running workflows past their own due date - Running workflows with an overdue task
With Date Range Statistics¶
When startDate and endDate are provided:
GET /srv.asmx/GetWorkflowStatistics?authenticationTicket=xxx&domainName=Engineering&workflowName=DocumentReview&startDate=2024-01-01&endDate=2024-01-31
Response includes all-time statistics PLUS date range specific data: - Submitted count in range - Completed count in range - Pending count in range
Response Structure¶
All-Time Statistics (No Dates)¶
<response success="true">
<Value>
<DomainName>Engineering</DomainName>
<WorkflowName>DocumentReview</WorkflowName>
<ActiveFolderPath>/Engineering/Reviews/Active</ActiveFolderPath>
<AverageTimeSpanInHours>27</AverageTimeSpanInHours>
<AverageTimeSpanInText>1 day 3 hours</AverageTimeSpanInText>
<TotalPending>15</TotalPending>
<TotalCompleted>892</TotalCompleted>
<TotalOverdue>2</TotalOverdue>
<TotalWithOverdueTasks>6</TotalWithOverdueTasks>
</Value>
</response>
With Date Range Statistics¶
<response success="true">
<Value>
<DomainName>Engineering</DomainName>
<WorkflowName>DocumentReview</WorkflowName>
<ActiveFolderPath>/Engineering/Reviews/Active</ActiveFolderPath>
<!-- Date range specific (only when dates provided) -->
<PendingInRange>3</PendingInRange>
<SubmittedInRange>45</SubmittedInRange>
<CompletedInRange>42</CompletedInRange>
<!-- All-time statistics -->
<AverageTimeSpanInHours>27</AverageTimeSpanInHours>
<AverageTimeSpanInText>1 day 3 hours</AverageTimeSpanInText>
<TotalPending>15</TotalPending>
<TotalCompleted>892</TotalCompleted>
<TotalOverdue>2</TotalOverdue>
<TotalWithOverdueTasks>6</TotalWithOverdueTasks>
</Value>
</response>
Error Response¶
WorkflowStatistics Properties¶
| Property | Type | Description | When Included |
|---|---|---|---|
DomainName |
string | Name of the domain | Always |
WorkflowName |
string | Name of the workflow | Always |
ActiveFolderPath |
string | Path to workflow's active folder | Always |
AverageTimeSpanInHours |
integer | The planned length of the workflow in hours: each step's longest task deadline, added up. It is read from the definition, not measured from runs, so it has a value when nothing has completed. Despite the name it is not an average. | Always |
AverageTimeSpanInText |
string | The same planned length as text, for example "8 Days" | Always |
TotalPending |
integer | Workflows of this definition that are running now (not finished) | Always |
TotalCompleted |
integer | Workflows of this definition that have finished, all-time | Always |
TotalOverdue |
integer | Running workflows past the workflow's own due date. Task due dates are not looked at. See How overdue is counted. | Always |
TotalWithOverdueTasks |
integer | Running workflows that have at least one task past its due date. Added in 9.0. See How overdue is counted. | Always |
PendingInRange |
integer (nullable) | Pending in date range | Only with dates |
SubmittedInRange |
integer (nullable) | Submitted in date range | Only with dates |
CompletedInRange |
integer (nullable) | Completed in date range | Only with dates |
How Overdue Is Counted¶
The response counts late workflows in two ways, and they answer different questions.
| Count | A running workflow is counted when | Use it for |
|---|---|---|
TotalOverdue |
the workflow's own due date has passed | "Which workflows should have finished by now?" |
TotalWithOverdueTasks |
it has at least one task past its due date | "Which workflows are stuck on a late task?" |
- The workflow's due date is set when a document is submitted: the submit time plus the planned length (
AverageTimeSpanInHours). It does not move when a task's due date is changed. - An overdue task is one that has started, is not finished, is in progress, and whose due date has passed. This is the same rule the server's task lists use when they are filtered to overdue tasks.
- A workflow is counted once in
TotalWithOverdueTaskshowever many of its tasks are late.
The two can differ a lot. With a planned length of 8 days and a first step due after 2, a workflow whose first task is three days late is in TotalWithOverdueTasks and not yet in TotalOverdue:
<TotalPending>9</TotalPending>
<TotalOverdue>0</TotalOverdue>
<TotalWithOverdueTasks>9</TotalWithOverdueTasks>
A running workflow that has no due date stored is never counted in TotalOverdue. Until 9.0 TotalOverdue was the only count, and it was described as the number of workflows with overdue tasks, which it is not.
Required Permissions¶
- User must be authenticated (anonymous users cannot access workflow statistics)
- User must have access to the specified domain
- No specific workflow permissions required to view statistics
Use Cases¶
- Single Workflow Monitor
- Track specific workflow performance
- Monitor real-time status
-
Alert on overdue instances
-
Monthly Performance Report
- Analyze workflow performance over time
- Compare month-over-month metrics
-
Track submission and completion trends
-
Dashboard Integration
- Real-time workflow status displays
- KPI monitoring
-
Performance metrics visualization
-
Capacity Planning
- Analyze workflow load
- Identify bottlenecks
- Plan resource allocation
Example Requests¶
Request (GET)¶
GET /srv.asmx/GetWorkflowStatistics?authenticationTicket=abc123-def456&domainName=Engineering&workflowName=DocumentReview HTTP/1.1
Host: server.example.com
Request (POST)¶
POST /srv.asmx/GetWorkflowStatistics HTTP/1.1
Content-Type: application/x-www-form-urlencoded
authenticationTicket=abc123-def456&domainName=Engineering&workflowName=DocumentReview
Request (SOAP 1.1)¶
POST /srv.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/GetWorkflowStatistics"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetWorkflowStatistics xmlns="http://tempuri.org/">
<authenticationTicket>abc123-def456</authenticationTicket>
<domainName>Engineering</domainName>
<workflowName>DocumentReview</workflowName>
<startDate></startDate>
<endDate></endDate>
</GetWorkflowStatistics>
</soap:Body>
</soap:Envelope>
Success Response Example¶
<?xml version="1.0" encoding="utf-8"?>
<response success="true">
<Value>
<DomainName>Quality</DomainName>
<WorkflowName>QualityApproval</WorkflowName>
<ActiveFolderPath>/Quality/Pending Approvals</ActiveFolderPath>
<AverageTimeSpanInHours>27</AverageTimeSpanInHours>
<AverageTimeSpanInText>1 day 3 hours</AverageTimeSpanInText>
<TotalPending>45</TotalPending>
<TotalCompleted>892</TotalCompleted>
<TotalOverdue>7</TotalOverdue>
<TotalWithOverdueTasks>11</TotalWithOverdueTasks>
</Value>
</response>
Integration Examples¶
JavaScript/Client-Side¶
async function getWorkflowStats(domainName, workflowName, startDate = null, endDate = null) {
const ticket = getUserAuthTicket();
let url = `/srv.asmx/GetWorkflowStatistics?` +
`authenticationTicket=${encodeURIComponent(ticket)}&` +
`domainName=${encodeURIComponent(domainName)}&` +
`workflowName=${encodeURIComponent(workflowName)}`;
if (startDate && endDate) {
url += `&startDate=${startDate.toISOString().split('T')[0]}`;
url += `&endDate=${endDate.toISOString().split('T')[0]}`;
}
const response = await fetch(url);
const xmlText = await response.text();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
const root = xmlDoc.documentElement;
if (root.getAttribute("success") === "true") {
const stats = xmlDoc.querySelector("Value");
return {
domainName: stats.querySelector("DomainName").textContent,
workflowName: stats.querySelector("WorkflowName").textContent,
activeFolderPath: stats.querySelector("ActiveFolderPath").textContent,
avgTimeHours: parseInt(stats.querySelector("AverageTimeSpanInHours").textContent),
avgTimeText: stats.querySelector("AverageTimeSpanInText").textContent,
totalPending: parseInt(stats.querySelector("TotalPending").textContent),
totalCompleted: parseInt(stats.querySelector("TotalCompleted").textContent),
totalOverdue: parseInt(stats.querySelector("TotalOverdue").textContent),
// Date range specific (if provided)
pendingInRange: stats.querySelector("PendingInRange")?.textContent
? parseInt(stats.querySelector("PendingInRange").textContent) : null,
submittedInRange: stats.querySelector("SubmittedInRange")?.textContent
? parseInt(stats.querySelector("SubmittedInRange").textContent) : null,
completedInRange: stats.querySelector("CompletedInRange")?.textContent
? parseInt(stats.querySelector("CompletedInRange").textContent) : null
};
} else {
const error = root.getAttribute("error");
throw new Error(error);
}
}
// Usage example
async function displayWorkflowMonitor() {
try {
const stats = await getWorkflowStats("Engineering", "DocumentReview");
console.log(`Workflow: ${stats.workflowName}`);
console.log(`Average Time: ${stats.avgTimeText} (${stats.avgTimeHours} hours)`);
console.log(`Pending: ${stats.totalPending}`);
console.log(`Completed: ${stats.totalCompleted}`);
console.log(`Overdue: ${stats.totalOverdue}`);
// Calculate efficiency
const totalFlows = stats.totalCompleted + stats.totalPending;
const completionRate = (stats.totalCompleted / totalFlows * 100).toFixed(1);
console.log(`Completion Rate: ${completionRate}%`);
// Alert on overdue
if (stats.totalOverdue > 0) {
alert(`Warning: ${stats.totalOverdue} overdue workflow instances!`);
}
} catch (error) {
console.error("Failed to get workflow statistics:", error);
}
}
C# Client Usage¶
using (var client = new SrvSoapClient())
{
try
{
var response = await client.GetWorkflowStatisticsAsync(
authTicket,
"Engineering",
"DocumentReview",
null, // startDate
null // endDate
);
var root = response.Root;
if (root.Attribute("success")?.Value == "true")
{
var stats = root.Element("Value");
var workflowStats = new
{
DomainName = stats.Element("DomainName")?.Value,
WorkflowName = stats.Element("WorkflowName")?.Value,
ActiveFolderPath = stats.Element("ActiveFolderPath")?.Value,
AvgTimeHours = int.Parse(stats.Element("AverageTimeSpanInHours")?.Value ?? "0"),
AvgTimeText = stats.Element("AverageTimeSpanInText")?.Value,
TotalPending = int.Parse(stats.Element("TotalPending")?.Value ?? "0"),
TotalCompleted = int.Parse(stats.Element("TotalCompleted")?.Value ?? "0"),
TotalOverdue = int.Parse(stats.Element("TotalOverdue")?.Value ?? "0")
};
Console.WriteLine($"Workflow: {workflowStats.WorkflowName} in {workflowStats.DomainName}");
Console.WriteLine($"Active Folder: {workflowStats.ActiveFolderPath}");
Console.WriteLine($"Average Time: {workflowStats.AvgTimeText} ({workflowStats.AvgTimeHours} hours)");
Console.WriteLine($"Pending: {workflowStats.TotalPending}");
Console.WriteLine($"Completed: {workflowStats.TotalCompleted}");
Console.WriteLine($"Overdue: {workflowStats.TotalOverdue}");
// Calculate metrics
int totalFlows = workflowStats.TotalCompleted + workflowStats.TotalPending;
double completionRate = totalFlows > 0
? (double)workflowStats.TotalCompleted / totalFlows * 100
: 0;
Console.WriteLine($"Completion Rate: {completionRate:F1}%");
if (workflowStats.TotalOverdue > 0)
{
Console.WriteLine($"ALERT: {workflowStats.TotalOverdue} overdue workflow instances");
}
}
else
{
var error = root.Attribute("error")?.Value;
Console.WriteLine($"Error: {error}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
}
Dashboard Integration¶
This API is ideal for building workflow monitoring dashboards:
<!-- Single Workflow Statistics Dashboard -->
<div class="workflow-stats">
<h2 id="workflow-title"></h2>
<div class="stats-grid">
<div class="stat-card">
<h3>Pending</h3>
<p id="pending-count" class="stat-value"></p>
</div>
<div class="stat-card">
<h3>Completed</h3>
<p id="completed-count" class="stat-value"></p>
</div>
<div class="stat-card warning">
<h3>Overdue</h3>
<p id="overdue-count" class="stat-value"></p>
</div>
<div class="stat-card">
<h3>Avg. Time</h3>
<p id="avg-time-text" class="stat-value"></p>
<small id="avg-time-hours"></small>
</div>
</div>
<div class="workflow-details">
<p><strong>Active Folder:</strong> <span id="active-folder"></span></p>
</div>
</div>
<script>
async function updateDashboard(domainName, workflowName) {
const stats = await getWorkflowStats(domainName, workflowName);
document.getElementById("workflow-title").textContent = `${stats.workflowName} (${stats.domainName})`;
document.getElementById("active-folder").textContent = stats.activeFolderPath;
document.getElementById("pending-count").textContent = stats.totalPending;
document.getElementById("completed-count").textContent = stats.totalCompleted;
document.getElementById("overdue-count").textContent = stats.totalOverdue;
document.getElementById("avg-time-text").textContent = stats.avgTimeText;
document.getElementById("avg-time-hours").textContent = `(${stats.avgTimeHours} hours)`;
}
// Refresh every 30 seconds
setInterval(() => updateDashboard("Engineering", "DocumentReview"), 30000);
updateDashboard("Engineering", "DocumentReview");
</script>
JavaScript¶
Every call answers XML with HTTP 200, success or not, so success is the thing to branch on and
errorCode is the number to report.
async function call(action, params) {
const response = await fetch(`/srv.asmx/${action}?${new URLSearchParams(params)}`);
const root = new DOMParser()
.parseFromString(await response.text(), 'text/xml')
.documentElement;
if (root.getAttribute('success') !== 'true') {
throw new Error(`${root.getAttribute('errorCode')}: ${root.getAttribute('error')}`);
}
return root;
}
Counts what a definition has done. Without a date range the three ...InRange counters come back as
xsi:nil; with one they carry a number.
const root = await call('GetWorkflowStatistics', {
authenticationTicket: ticket,
domainName: 'Public',
workflowName: 'Invoice approval',
startDate: '2026-01-01',
endDate: '2026-12-31'
});
const value = root.querySelector('Value');
console.log(value.querySelector('TotalPending').textContent,
value.querySelector('TotalCompleted').textContent,
value.querySelector('TotalOverdue').textContent,
value.querySelector('SubmittedInRange').textContent,
value.querySelector('AverageTimeSpanInHours').textContent);
Notes¶
- TotalCompleted: All workflow instances that have reached completion
- TotalPending: Workflow instances currently in progress (all-time)
- TotalOverdue: Workflow instances with overdue tasks
- AverageTimeSpanInText: Human-readable format (e.g., "2 days 5 hours")
- AverageTimeSpanInHours: Numeric value for programmatic comparisons
- PendingInRange, SubmittedInRange, CompletedInRange: Only present when date range provided
- Real-time Data: Statistics are calculated in real-time from current database state
- Performance: Optimized for single workflow - faster than bulk reporting
Statistics Calculations¶
All-Time Metrics¶
- TotalCompleted: Counts all workflow instances that reached completion
- TotalPending: Counts workflow instances at any step (not completed)
- TotalOverdue: Based on task due dates compared to current date
Date Range Metrics (Optional)¶
- SubmittedInRange: Workflows submitted during the specified period
- CompletedInRange: Workflows completed during the specified period
- PendingInRange: Workflows still pending within the date range
Average TimeSpan¶
- Calculated from workflow start to completion
- Based on completed workflow instances only
- AverageTimeSpanInText: Human-readable string
- AverageTimeSpanInHours: Numeric value for calculations
- May return 0 or empty string if no completed instances exist
Best Practices¶
- Caching: Cache results for 1-5 minutes for dashboard displays
- Monitor Overdue: Set up alerts when overdue count exceeds threshold
- Date Range Queries: Use for monthly/quarterly reporting
- Polling Frequency: For real-time dashboards, poll every 30-60 seconds
- Error Handling: Always handle workflow not found and access denied errors
- Performance: Single workflow queries are fast and efficient
Performance Considerations¶
- Optimized: Single workflow statistics are calculated quickly
- Real-Time: No caching - always returns current state
- Efficient: Significantly faster than bulk reporting
- Date Range Impact: Date range queries may take slightly longer
- Recommended: For bulk reporting, call API multiple times for different workflows
Benefits¶
? Fast Performance: Optimized for single workflow queries
? Real-Time Data: Always returns current workflow state
? Flexible: Supports all-time or date range statistics
? Efficient: Minimal server load for targeted queries
? Simple: Returns single object, easy to parse and use
? Comprehensive: All key workflow metrics in one call
Related APIs¶
GetFlowDef- Get detailed workflow definitionGetAllWorkflowStatistics- The statistics of every workflow of a domain, or of all domains, in one call (no call per workflow)GetDomainFlows- List all workflows in a domaingetTasks- Get workflow tasks with filteringGetTask- Get specific task detailsCompleteTask- Complete a workflow taskReassignTask- Reassign task to different user
Version History¶
- Compatible with infoRouter 8.7 and later
- Statistics model is serializable for client-side deserialization
- Supports both synchronous SOAP and REST access patterns
- Workflow management features may require Workflow module license
See Also¶
- GetFlowDef - Get workflow definition details
- GetDomainFlows - List all workflows in domain
- getTasks - Get workflow tasks
- Control Panel UI:
WorkflowReport.aspx- Workflow statistics report
Error Codes¶
The errorCode values this operation returns, checked against a running server:
errorCode |
When |
|---|---|
4010 |
the ticket is expired or unknown |
4000 |
no definition by that name in the library |
4030 |
the caller may not see that library |
HTTP 400 |
a required string parameter was empty; refused by model binding, so there is no error document |