Resume Process
Overview
The ResumeProcessInstanceCommand resumes a paused process instance and continues execution from where it was paused. This is useful for processes that need temporary suspension.
Pause/Resume Requirement
This command requires that the process was explicitly paused. It cannot resume processes that are waiting at UserTasks or in error states - use SignalProcessInstanceCommand or RetryFailedTaskCommand for those scenarios.
API Endpoint
POST /api/core/cmd
Headers
Content-Type: application/json
Authorization: Bearer {access_token}
X-Tenant-ID: {tenant_id}
Request Structure
{
"cmd": "ResumeProcessInstanceCommand",
"data": {
"instanceGuid": "abc-123-def-456",
"resumeComment": "External approval received, resuming process",
"updatedVariables": {
"approvalReceived": true,
"approvalTimestamp": "2024-12-19T11:00:00Z"
}
}
}
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
instanceGuid | string | Yes | The unique identifier of the paused process instance |
resumeComment | string | No | Reason for resuming (for audit trail) |
updatedVariables | object | No | Variables to update before resuming |
When to Use
✅ Good Use Cases
- External Approval: Resume after receiving external approval/authorization
- Resource Availability: Resume when required resources become available
- Scheduled Pause: Resume after planned maintenance or scheduled event
- Batch Processing: Pause during low-priority periods, resume during off-peak
- Manual Intervention Complete: Resume after manual review or correction
❌ Wrong Use Cases
- Waiting at UserTask: Use
SignalProcessInstanceCommandinstead - Process in Error: Use
RetryFailedTaskCommandorSkipFailedTaskCommand - Not Started: Use
StartProcessInstanceCommandto start a new process - Already Completed: Cannot resume completed processes
Sample Requests
1. Resume After External Approval
{
"cmd": "ResumeProcessInstanceCommand",
"data": {
"instanceGuid": "loan-app-2024-001",
"resumeComment": "Board approval received for high-value loan",
"updatedVariables": {
"boardApprovalReceived": true,
"approvalDate": "2024-12-19T11:00:00Z",
"approvedBy": "Board of Directors"
}
}
}
2. Resume After Resource Becomes Available
{
"cmd": "ResumeProcessInstanceCommand",
"data": {
"instanceGuid": "bulk-import-456",
"resumeComment": "Database maintenance completed, resuming import",
"updatedVariables": {
"maintenanceCompleted": true,
"resumedAt": "2024-12-19T11:15:00Z"
}
}
}
3. Simple Resume Without Updates
{
"cmd": "ResumeProcessInstanceCommand",
"data": {
"instanceGuid": "account-opening-789",
"resumeComment": "Manual verification complete"
}
}
Response Structure
Successfully Resumed and Continued
{
"isSuccessful": true,
"message": "Process instance resumed successfully.",
"statusCode": "00",
"data": {
"instanceGuid": "loan-app-2024-001",
"status": "running",
"currentActivityId": "Task_ProcessLoan",
"resumedAt": "2024-12-19T11:00:00Z",
"pauseDuration": 7200000,
"result": {
"message": "Process resumed and continuing execution",
"waiting": false
},
"state": {
"variables": {
"loanId": 789,
"customerId": 456,
"loanAmount": 500000,
"boardApprovalReceived": true,
"approvalDate": "2024-12-19T11:00:00Z"
},
"currentNode": "Task_ProcessLoan",
"completedTasks": [
"StartEvent",
"Task_ValidateCustomer",
"Task_CreditCheck"
]
}
}
}
Resumed and Waiting at Next UserTask
{
"isSuccessful": true,
"message": "Process resumed and waiting at next UserTask.",
"statusCode": "00",
"data": {
"instanceGuid": "loan-app-2024-001",
"status": "waiting",
"currentActivityId": "Task_FinalReview",
"resumedAt": "2024-12-19T11:00:00Z",
"currentTask": {
"taskId": "Task_FinalReview",
"taskName": "Final Review",
"taskType": "UserTask"
},
"nextActions": [
{
"action": "approve",
"label": "Approve Loan"
},
{
"action": "reject",
"label": "Reject Loan"
}
]
}
}
Error Responses
Process Not Paused
{
"isSuccessful": false,
"message": "Process instance is not paused. Current state: running",
"statusCode": "99",
"data": {
"instanceGuid": "loan-app-2024-001",
"currentStatus": "running"
}
}
Process Not Found
{
"isSuccessful": false,
"message": "Process instance 'invalid-guid' not found.",
"statusCode": "99",
"data": null
}
Process Already Completed
{
"isSuccessful": false,
"message": "Cannot resume process instance - already completed.",
"statusCode": "99",
"data": {
"instanceGuid": "loan-app-2024-001",
"status": "completed"
}
}
Pause/Resume Lifecycle
Use Cases
1. Resume After External Event
class ExternalApprovalHandler {
constructor() {
this.pendingApprovals = new Map();
}
async pauseForApproval(instanceGuid, approvalType) {
// Pause the process
await pauseProcess({
instanceGuid,
reason: `Waiting for ${approvalType} approval`
});
// Track pending approval
this.pendingApprovals.set(instanceGuid, {
type: approvalType,
pausedAt: new Date(),
status: 'pending'
});
// Send approval request
await this.requestApproval(instanceGuid, approvalType);
}
async handleApprovalResponse(instanceGuid, approved, approver) {
const approval = this.pendingApprovals.get(instanceGuid);
if (!approval) {
throw new Error(`No pending approval found for ${instanceGuid}`);
}
if (approved) {
// Resume with approval data
const response = await resumeProcess({
instanceGuid,
resumeComment: `${approval.type} approved by ${approver}`,
updatedVariables: {
[`${approval.type}_approved`]: true,
[`${approval.type}_approver`]: approver,
[`${approval.type}_date`]: new Date().toISOString()
}
});
this.pendingApprovals.delete(instanceGuid);
return response;
} else {
// Cancel process if rejected
await cancelProcess({
instanceGuid,
reason: `${approval.type} rejected by ${approver}`
});
this.pendingApprovals.delete(instanceGuid);
}
}
async requestApproval(instanceGuid, approvalType) {
// Send to approval system
await approvalSystem.request({
processInstance: instanceGuid,
type: approvalType,
callback: (approved, approver) => {
this.handleApprovalResponse(instanceGuid, approved, approver);
}
});
}
}
// Usage
const approvalHandler = new ExternalApprovalHandler();
// Pause for approval
await approvalHandler.pauseForApproval('loan-app-2024-001', 'board_approval');
// Later, when approval received
await approvalHandler.handleApprovalResponse('loan-app-2024-001', true, 'CEO');
2. Scheduled Resume
class ScheduledResumeManager {
constructor() {
this.scheduledResumes = new Map();
}
async pauseUntil(instanceGuid, resumeTime, reason) {
// Pause the process
await pauseProcess({
instanceGuid,
reason: `${reason} - scheduled resume at ${resumeTime}`
});
// Calculate delay
const delay = new Date(resumeTime) - new Date();
if (delay <= 0) {
throw new Error("Resume time must be in the future");
}
// Schedule resume
const timeoutId = setTimeout(
() => this.executeScheduledResume(instanceGuid, reason),
delay
);
this.scheduledResumes.set(instanceGuid, {
timeoutId,
resumeTime,
reason
});
console.log(`Scheduled resume for ${instanceGuid} at ${resumeTime}`);
}
async executeScheduledResume(instanceGuid, reason) {
try {
const response = await resumeProcess({
instanceGuid,
resumeComment: `Scheduled resume: ${reason}`,
updatedVariables: {
resumedAt: new Date().toISOString(),
resumeType: 'scheduled'
}
});
if (response.isSuccessful) {
console.log(`Successfully resumed ${instanceGuid}`);
} else {
console.error(`Failed to resume ${instanceGuid}:`, response.message);
}
} catch (error) {
console.error(`Error resuming ${instanceGuid}:`, error);
} finally {
this.scheduledResumes.delete(instanceGuid);
}
}
cancelScheduledResume(instanceGuid) {
const scheduled = this.scheduledResumes.get(instanceGuid);
if (scheduled) {
clearTimeout(scheduled.timeoutId);
this.scheduledResumes.delete(instanceGuid);
console.log(`Cancelled scheduled resume for ${instanceGuid}`);
}
}
}
// Usage
const resumeManager = new ScheduledResumeManager();
// Pause until tomorrow 9 AM
const tomorrow9AM = new Date();
tomorrow9AM.setDate(tomorrow9AM.getDate() + 1);
tomorrow9AM.setHours(9, 0, 0, 0);
await resumeManager.pauseUntil(
'batch-process-001',
tomorrow9AM,
'Waiting for business hours'
);
3. Resume with Resource Check
async function resumeWhenResourceAvailable(instanceGuid, resourceType) {
const maxAttempts = 10;
const checkInterval = 5000; // 5 seconds
for (let attempt = 0; attempt < maxAttempts; attempt++) {
// Check if resource is available
const available = await checkResourceAvailability(resourceType);
if (available) {
console.log(`Resource ${resourceType} available, resuming process`);
return await resumeProcess({
instanceGuid,
resumeComment: `Resource ${resourceType} became available`,
updatedVariables: {
resourceAvailableAt: new Date().toISOString(),
attemptsRequired: attempt + 1
}
});
}
console.log(`Resource ${resourceType} not available, attempt ${attempt + 1}/${maxAttempts}`);
// Wait before next check
if (attempt < maxAttempts - 1) {
await sleep(checkInterval);
}
}
throw new Error(`Resource ${resourceType} not available after ${maxAttempts} attempts`);
}
async function checkResourceAvailability(resourceType) {
switch (resourceType) {
case 'database':
return await isDatabaseHealthy();
case 'api':
return await isAPIAvailable();
case 'file_storage':
return await isStorageAccessible();
default:
return false;
}
}
// Usage
await pauseProcess({
instanceGuid: 'import-job-001',
reason: 'Database maintenance in progress'
});
// Resume when database is back
await resumeWhenResourceAvailable('import-job-001', 'database');
4. Batch Resume Operations
async function batchResumeProcesses(criteria) {
// Find paused processes matching criteria
const pausedProcesses = await findPausedProcesses(criteria);
console.log(`Found ${pausedProcesses.length} paused processes to resume`);
const results = {
successful: [],
failed: []
};
// Resume processes in parallel (with concurrency limit)
const concurrency = 5;
const chunks = chunkArray(pausedProcesses, concurrency);
for (const chunk of chunks) {
const promises = chunk.map(async (process) => {
try {
const response = await resumeProcess({
instanceGuid: process.instanceGuid,
resumeComment: `Batch resume: ${criteria.reason}`,
updatedVariables: {
batchResumed: true,
batchId: criteria.batchId
}
});
if (response.isSuccessful) {
results.successful.push(process.instanceGuid);
} else {
results.failed.push({
guid: process.instanceGuid,
error: response.message
});
}
} catch (error) {
results.failed.push({
guid: process.instanceGuid,
error: error.message
});
}
});
await Promise.all(promises);
}
console.log(`Resumed ${results.successful.length} processes successfully`);
console.log(`Failed to resume ${results.failed.length} processes`);
return results;
}
function chunkArray(array, size) {
const chunks = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
async function findPausedProcesses(criteria) {
return await db.query(`
SELECT instanceGuid, pausedAt, pauseReason
FROM ProcessInstances
WHERE status = 'paused'
AND processType = ?
AND pausedAt < ?
`, [criteria.processType, criteria.pausedBefore]);
}
// Usage: Resume all processes paused during maintenance
await batchResumeProcesses({
processType: 'loan_application',
pausedBefore: new Date('2024-12-19T08:00:00Z'),
reason: 'Maintenance completed',
batchId: 'MAINTENANCE-2024-12-19'
});
Best Practices
1. Verify Process is Paused Before Resuming
async function safeResumeProcess(instanceGuid, resumeComment, updatedVariables) {
// Check current state
const state = await getProcessState(instanceGuid);
if (!state.isSuccessful) {
throw new Error(`Process ${instanceGuid} not found`);
}
const status = state.data.status;
if (status !== "paused") {
if (status === "waiting") {
throw new Error("Process is waiting at UserTask - use SignalProcessInstanceCommand");
} else if (status === "error") {
throw new Error("Process is in error state - use RetryFailedTaskCommand or SkipFailedTaskCommand");
} else if (status === "completed") {
throw new Error("Process already completed - cannot resume");
} else {
throw new Error(`Process is not paused (current status: ${status})`);
}
}
// Safe to resume
return await resumeProcess({
instanceGuid,
resumeComment,
updatedVariables
});
}
2. Track Pause/Resume Duration
async function resumeWithMetrics(instanceGuid, resumeComment) {
const state = await getProcessState(instanceGuid);
const pausedAt = new Date(state.data.pausedAt);
const now = new Date();
const pauseDuration = now - pausedAt;
const response = await resumeProcess({
instanceGuid,
resumeComment,
updatedVariables: {
pauseDuration,
pausedAt: state.data.pausedAt,
resumedAt: now.toISOString()
}
});
// Log metrics
await metrics.record({
metric: 'process_pause_duration',
value: pauseDuration,
instanceGuid,
processType: state.data.state.variables.processType
});
// Alert if pause was very long
if (pauseDuration > 24 * 60 * 60 * 1000) { // 24 hours
await alertManagement({
severity: 'WARNING',
message: `Process ${instanceGuid} was paused for ${Math.round(pauseDuration / (60 * 60 * 1000))} hours`,
reason: resumeComment
});
}
return response;
}
3. Handle Resume Failures Gracefully
async function resumeWithRetry(instanceGuid, resumeComment, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await resumeProcess({
instanceGuid,
resumeComment: `${resumeComment} (attempt ${attempt + 1})`,
updatedVariables: {
resumeAttempt: attempt + 1
}
});
if (response.isSuccessful) {
return response;
}
console.warn(`Resume attempt ${attempt + 1} failed:`, response.message);
// Wait before retry
if (attempt < maxRetries - 1) {
await sleep(1000 * (attempt + 1)); // Exponential backoff
}
} catch (error) {
console.error(`Resume attempt ${attempt + 1} error:`, error);
if (attempt === maxRetries - 1) {
throw error;
}
}
}
throw new Error(`Failed to resume after ${maxRetries} attempts`);
}
4. Log All Resume Operations
async function resumeWithAudit(instanceGuid, resumeComment, updatedVariables, resumedBy) {
const startTime = Date.now();
// Get state before resume
const beforeState = await getProcessState(instanceGuid);
// Perform resume
const response = await resumeProcess({
instanceGuid,
resumeComment,
updatedVariables
});
// Log to audit system
await auditLog.record({
action: 'RESUME_PROCESS',
instanceGuid,
resumeComment,
resumedBy,
timestamp: new Date(),
duration: Date.now() - startTime,
beforeState: {
status: beforeState.data.status,
pausedAt: beforeState.data.pausedAt,
pauseDuration: Date.now() - new Date(beforeState.data.pausedAt)
},
afterState: {
status: response.data?.status,
currentTask: response.data?.currentActivityId
},
success: response.isSuccessful,
updatedVariables: Object.keys(updatedVariables || {})
});
return response;
}
Related Commands
- Pause Process - Pause a running process (if available)
- Signal Process - Resume waiting processes at UserTasks
- Retry Failed Task - Resume processes in error state
- Get Process State - Check if process is paused
Notes
- Only works when process is in "paused" state
- Cannot resume processes waiting at UserTasks - use
SignalProcessInstanceCommand - Cannot resume processes in error state - use
RetryFailedTaskCommand - Updated variables are merged into process context before resuming
- Resume comment is stored in audit logs
- Process continues from where it was paused
- All process variables and state are preserved during pause
- Consider tracking pause duration for metrics
- Implement retry logic for resume failures
- Verify process state before attempting resume
- Use scheduled resume for time-based pauses