Paper approvals drag on for weeks. Documents get lost, signatures are forged, employees waste time on constant reminders. We develop digital approval workflows that end this chaos. Our engine embeds into a website or CRM, automatically routes documents along predefined paths, controls deadlines, and maintains a full audit log. No more "lost in email" — everything is transparent in one window. Over 5 years, we have implemented 50+ projects, reducing approval time by an average of 3x. More about the workflow concept can be read on Wikipedia.
Problems solved by approval workflow
If a manager is on a business trip, the process stalls. Our engine automatically escalates the task to a deputy or reassigns it based on rules. Timeouts are configurable in hours — if no response, the system reminds and then escalates higher. Manual control consumes hours: employees track statuses manually. We provide a personal cabinet with a timeline showing every step: who, when, and what they decided. The initiator sees progress, approvers see the deadline. Errors and duplicates are eliminated: the workflow strictly defines the route with role verification. In one project, we reduced errors by 90%. Integration with EDS (electronic signature) is implemented via the signatories' API, supporting qualified and non-qualified certificates. Transitioning to electronic document management provides full transparency — this is the document signing system on the website.
How we build flexible workflow?
We use a proven stack: TypeScript + Node.js (Nest.js) on the backend, React with TypeScript on the frontend. For storage — PostgreSQL with JSONB for step templates. The state engine is implemented as a separate microservice with clear transactions.
Data model — workflow templates, instances for each document, and approval tasks. Each task is linked to a step, executor, and timeout. JSONB in the template allows flexible step description: type (sequential/parallel), transition conditions, escalation.
-- Шаблоны workflow
CREATE TABLE workflow_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(200),
description TEXT,
steps JSONB NOT NULL, -- Массив шагов с конфигурацией
created_by UUID REFERENCES users(id)
);
-- Экземпляр workflow для конкретного документа
CREATE TABLE workflow_instances (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
template_id UUID REFERENCES workflow_templates(id),
document_id UUID REFERENCES documents(id),
initiator_id UUID REFERENCES users(id),
current_step INT DEFAULT 1,
status VARCHAR(50) DEFAULT 'in_progress', -- in_progress, approved, rejected, cancelled
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
completed_at TIMESTAMPTZ
);
-- Задачи согласования
CREATE TABLE workflow_tasks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
instance_id UUID REFERENCES workflow_instances(id),
step_number INT NOT NULL,
assignee_id UUID REFERENCES users(id),
assignee_role VARCHAR(100), -- Альтернатива assignee_id для динамических ролей
task_type VARCHAR(50), -- 'approve', 'sign', 'review'
status VARCHAR(50) DEFAULT 'pending', -- pending, approved, rejected, delegated
comment TEXT,
due_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
completed_by_id UUID REFERENCES users(id) -- Если делегировал
);
The engine processes decisions: approve — checks parallel step (all approved?), then moves to next step or completes workflow. reject — stops the process or returns for revision. request_changes — sends document to initiator with comment.
class WorkflowEngine {
async processDecision(
taskId: string,
decision: 'approve' | 'reject' | 'request_changes',
comment: string,
userId: string
) {
const task = await db.workflowTasks.findOne(taskId, { include: 'instance.template' });
if (task.assigneeId !== userId) throw new Error('Not authorized');
await db.workflowTasks.update(taskId, {
status: decision,
comment,
completedAt: new Date(),
});
await auditLog.record({
action: `task.${decision}`,
userId,
taskId,
instanceId: task.instanceId,
});
switch (decision) {
case 'approve':
await this.onTaskApproved(task);
break;
case 'reject':
await this.onTaskRejected(task);
break;
case 'request_changes':
await this.returnToInitiator(task, comment);
break;
}
}
private async onTaskApproved(task: WorkflowTask) {
const instance = task.instance;
const template = JSON.parse(instance.template.steps);
const currentStep = template[task.stepNumber - 1];
// Параллельный шаг: проверяем все ли в этом шаге одобрили
if (currentStep.type === 'parallel') {
const stepTasks = await db.workflowTasks.findAll({
instanceId: instance.id,
stepNumber: task.stepNumber,
});
const allApproved = stepTasks.every(t => t.status === 'approve');
const anyRejected = stepTasks.some(t => t.status === 'reject');
if (anyRejected) return this.onTaskRejected(task);
if (!allApproved) return; // Ждём остальных
}
// Переходим к следующему шагу
const nextStep = template[task.stepNumber]; // Следующий элемент
if (!nextStep) {
// Все шаги пройдены — workflow завершён
await this.completeWorkflow(instance.id);
} else {
await this.activateStep(instance.id, nextStep, task.stepNumber + 1);
}
}
private async activateStep(instanceId: string, step: WorkflowStep, stepNumber: number) {
await db.workflowInstances.update(instanceId, { currentStep: stepNumber });
const assignees = await this.resolveAssignees(step);
const dueAt = step.deadlineHours ? addHours(new Date(), step.deadlineHours) : null;
for (const assignee of assignees) {
const task = await db.workflowTasks.create({
instanceId,
stepNumber,
assigneeId: assignee.id,
taskType: step.taskType,
dueAt,
});
await notifyAssignee(assignee, task);
}
}
}
How delegation and escalation work?
The approver can transfer a task to a colleague with a comment. The system closes the original task and creates a new one for the delegate. All delegations are recorded in the audit log.
async function delegateTask(taskId, delegateToId, reason, requesterId) {
const task = await db.workflowTasks.findByPk(taskId);
if (task.assigneeId !== requesterId) throw new Error('Not authorized');
// Закрываем текущую задачу
await db.workflowTasks.update(taskId, {
status: 'delegated',
comment: `Делегировано: ${reason}`,
completedAt: new Date(),
});
// Создаём новую для делегата
await db.workflowTasks.create({
...task.toJSON(),
id: undefined,
assigneeId: delegateToId,
status: 'pending',
completedAt: null,
metadata: { delegatedFrom: task.assigneeId, reason },
});
await notifyDelegate(delegateToId, taskId);
}
Timeouts are checked by a cron job every hour. If a task is overdue — a notification is sent to the approver and escalation to the manager. Auto-approval on timeout can be configured.
// Cron job: проверяем просроченные задачи каждый час
async function processOverdueTasks() {
const overdueTasks = await db.workflowTasks.findAll({
status: 'pending',
dueAt: { lt: new Date() },
escalationSentAt: null,
});
for (const task of overdueTasks) {
const step = getStepConfig(task);
if (step.escalationUserId) {
// Уведомляем руководителя
await notifyEscalation(step.escalationUserId, task);
await db.workflowTasks.update(task.id, { escalationSentAt: new Date() });
}
if (step.autoApproveOnTimeout) {
await workflowEngine.processDecision(task.id, 'approve', 'Auto-approved on timeout', 'system');
}
}
}
Progress visualization
The initiator sees the workflow timeline: which step is completed, who approved, who hasn't responded yet, how long we're waiting. A React component with a vertical list, status icons (✓, ✗, ⏳), and tooltips with comments.
Notifications
| Event | To Whom | Urgency |
|---|---|---|
| Task assigned | Approver | Immediate |
| Deadline in 4h | Approver | Push + Email |
| Task overdue | Approver + escalation | |
| Document approved | Initiator | In-app + Email |
| Document rejected | Initiator | Immediate, all channels |
Why implement workflow on the website?
Workflow on our engine is 3 times faster than paper document flow and 50% more efficient than simple email notifications. You get transparency, control, and resource savings. A typical project pays for itself in 3–6 months by reducing employee time. Average savings on operational expenses can be substantial. We guarantee quality: 5 years of experience, certified engineers, 12 months of support after implementation.
What is included in the work?
- Analytics: study business processes, roles, and document types.
- Design: create a workflow schema with steps, conditions, and roles.
- Implementation: develop the engine, integration with document storage and EDS.
- Interface: personal cabinet for initiator and approvers.
- Testing: unit test coverage, load testing.
- Documentation: API description, user instructions, administration.
- Training: webinar for key users.
- Support: 2 weeks of free post-production support.
Estimated timelines
| Configuration | Timeline |
|---|---|
| Basic workflow (sequential approval, tasks, notifications) | 7–10 days |
| Extended (parallel, delegation, escalation, timeouts) | 12–17 days |
| With visual template builder | 19–27 days |
Exact timelines are calculated after an audit of your processes. Contact us — we'll conduct a free audit and propose a solution within 1 day. Order implementation and reduce approval time by 3 times. Get a consultation today.







