Building Tailored Approval Workflows: Designing Portals That Match Your Company's Exact Logic
A deep dive into architecting enterprise-grade approval workflow engines: eliminating rigid CRM bottlenecks, implementing parallel Directed Acyclic Graphs (DAGs), dynamic SLA escalations, and immutable SOC 2 audit trails.

In mid-market and enterprise organizations, deal velocity rarely stalls because of sales hesitation or customer disinterest. It stalls inside the company's own internal approval queues.
Standard off-the-shelf CRM platforms (such as Salesforce, HubSpot, or Jira Service Management) offer built-in "approval builders." In theory, these visual tools promise point-and-click configuration of business rules. In practice, they are rigid, linear waterfalls engineered for simplistic corporate hierarchies.
When enterprise reality intrudes—complex margin thresholds, parallel reviews by disparate legal and finance stakeholders, out-of-office delegations, regional territory splits, and strict compliance sign-offs—commercial workflow engines fail catastrophically.
A deal offering a 24% discount might require parallel sign-offs from the Regional Sales Director and the VP of Finance. If Legal requests a one-line redline to an indemnification clause, generic CRM workflows wipe the entire approval state, forcing reps to restart the multi-day chain from scratch. Worse, commercial tools demand full per-seat subscriptions (165–300/user/month) for executives or external legal counsel who only need to review five contracts a quarter.
Solving this operational bottleneck requires architecting bespoke approval workflow engines inside custom business portals.
By modeling approval lifecycles as deterministic Directed Acyclic Graphs (DAGs) backed by PostgreSQL 16 and asynchronous event queues, engineering teams can build approval systems that match their organization's exact business logic—eliminating deal slippage, enforcing Segregation of Duties (SoD), and accelerating approval cycles from days to minutes.
[Visual Asset: Architecture Schematic - Directed Acyclic Graph (DAG) Multi-Stage Approval Engine]
flowchart TD
subgraph INITIATION [400 font-semibold">class="text-emerald-300">"1. Submission & Precondition Gate"]
DEAL[400 font-semibold">class="text-emerald-300">"Deal Submitted by Account Executive\n(Discount: 25%, Payment: Net-60)"]
RULE_EVAL[400 font-semibold">class="text-emerald-300">"JSONB Rule Evaluation Engine\n(Checks Margin, Billing Terms, Customer Tier)"]
DEAL --> RULE_EVAL
end
subgraph STAGE_1 [400 font-semibold">class="text-emerald-300">"Stage 1: Territory & Sales Governance"]
DIR_REV[400 font-semibold">class="text-emerald-300">"Regional Sales Director Approval\n(SLA: 4 Hours)"]
RULE_EVAL -->|Rule Match: Discount > 15%| DIR_REV
end
subgraph STAGE_2_PARALLEL [400 font-semibold">class="text-emerald-300">"Stage 2: Parallel Quorum Evaluation (AND Logic)"]
direction TB
subgraph FORK_LEGAL [400 font-semibold">class="text-emerald-300">"Branch A: Legal Review"]
LEGAL[400 font-semibold">class="text-emerald-300">"Legal Counsel Redline Sign-off\n(Net-60 Terms & Indemnity)"]
LEGAL_ACTION{400 font-semibold">class="text-emerald-300">"Verdict?"}
LEGAL --> LEGAL_ACTION
end
subgraph FORK_FINANCE [400 font-semibold">class="text-emerald-300">"Branch B: Finance & Margin Review"]
VP_FINANCE[400 font-semibold">class="text-emerald-300">"VP of Finance Margin Sign-off\n(Gross Margin Floor > 65%)"]
FIN_ACTION{400 font-semibold">class="text-emerald-300">"Verdict?"}
VP_FINANCE --> FIN_ACTION
end
DIR_REV -->|Stage 1 Approved| FORK_LEGAL
DIR_REV -->|Stage 1 Approved| FORK_FINANCE
end
subgraph RESOLUTION_ENGINE [400 font-semibold">class="text-emerald-300">"3. Convergence & Remediation Engine"]
direction TB
CONVERGE[400 font-semibold">class="text-emerald-300">"DAG Consensus Barrier\n(Requires Both Branches Confirmed)"]
REDLINE[400 font-semibold">class="text-emerald-300">"Targeted Remediation Loop\n(Contract Redlines Returned to Rep\nPreserving Finance Approval)"]
LEGAL_ACTION -->|Approved| CONVERGE
LEGAL_ACTION -->|Changes Requested| REDLINE
FIN_ACTION -->|Approved| CONVERGE
FIN_ACTION -->|Rejected| TERMINAL_LOST[400 font-semibold">class="text-emerald-300">"Deal Rejected / Escalated to CFO"]
REDLINE -.->|Revised Document Submitted| LEGAL
end
subgraph EXECUTION [400 font-semibold">class="text-emerald-300">"4. Atomic Execution & Event Broadcast"]
CONVERGE -->|Unanimous Quorum| COMMIT[400 font-semibold">class="text-emerald-300">"Atomic State Transition: 'Contract Executed'"]
COMMIT --> STRIPE_SYNC[400 font-semibold">class="text-emerald-300">"Stripe Invoicing / Contract Generation"]
COMMIT --> AUDIT_LOG[(400 font-semibold">class="text-emerald-300">"Partitioned SOX Audit Log Insert")]
COMMIT --> SLACK_NOTIF[400 font-semibold">class="text-emerald-300">"Broadcast Slack & Mobile Webhooks"]
end
+---------------------------------------------------------------------------------------------------------+
| PARALLEL DAG APPROVAL CONVERGENCE TOPOLOGY |
+---------------------------------------------------------------------------------------------------------+
| |
| [ Account Executive ] |
| │ (Submits $250k Contract: 22% Discount, Custom SLA, Net-60) |
| ▼ |
| [ Rule Evaluation Engine ] ──► Dynamically compiles required approval graph |
| │ |
| ▼ |
| [ Stage 1: Regional Sales Director ] ──► Approved within 45 mins |
| │ |
| ├────────────────────────────────────────┬────────────────────────────────────────┐ |
| ▼ ▼ ▼ |
| [ Branch A: Legal Review ] [ Branch B: Finance Review ] [ SLA Watchdog Timer ]|
| - MSA Terms & Redlines - Gross Margin Verification - Redis Delayed Stream|
| - Omnichannel Magic Link Review - Slack Block Kit Interactive Action - 4-Hour Escalation |
| │ │ │ |
| ▼ ▼ ▼ |
| [ Status: Clause Updated ] [ Status: Margin Approved ] [ Active SLA: 1.8h ]|
| │ │ |
| └────────────────────────────────────────┴────────────────────────────────────────┐ |
| ▼ |
| [ Consensus Barrier ] |
| (Parallel Convergence) |
| │ |
| ▼ |
| [ Final Execution ] |
| - Lock Deal Parameters |
| - Generate Digital Hash |
| - Trigger ERP / Stripe |
| |
+---------------------------------------------------------------------------------------------------------+
| METRICS: Zero full-chain restarts on redlines | Parallel consensus | Sub-second state synchronization |
+---------------------------------------------------------------------------------------------------------+
1. The Fragility of Off-the-Shelf Approval Systems#
Commercial CRMs treat approval routing as a secondary administrative feature. Because their visual builders are designed for non-technical administrators, they abstract away the underlying graph theory required for mission-critical corporate operations.
When businesses scale their sales and operations, five structural failures inevitably emerge:
A. The Sequential Waterfall Bottleneck#
Commercial tools enforce linear sequences: User A\rightarrow User B \rightarrow User C. If a multi-million-dollar deal requires sign-off from both the Information Security team (reviewing a SOC 2 addendum) and the Finance team (reviewing payment terms), a sequential tool forces Finance to wait days until InfoSec completes its review.
In a bespoke portal, approvals execute in parallel branches. Both departments review the deal concurrently. The system only unblocks the next stage when all required parallel criteria reach quorum.
B. The "All-or-Nothing" Restart Catastrophe#
In Salesforce or HubSpot, an approval request has binary states:Approved or Rejected. If Legal approves the terms but Finance requests that the billing cycle be changed from semi-annual to quarterly, pressing "Reject" wipes out Legal’s sign-off. The rep must re-edit the proposal and re-route the entire document through Legal again.
A bespoke portal supports targeted remediation states (REQUESTED_CHANGES on specific metadata nodes). Finance can request changes to payment terms without invalidating Legal's executed redline approval.
C. Out-of-Office Black Holes & Stuck Deals#
Quarter-end deal cycles routinely stall because an authorized director is boarding a long-haul flight or on medical leave. Standard platforms either freeze the transaction or require a global system administrator to manually reassign the record.A custom approval engine implements dynamic surrogate delegation and automated SLA escalation:
- If an approver has activated Out-of-Office (OOO) status in their profile, incoming requests route automatically to designated peer delegates.
- If an approval remains unacknowledged after a configurable Time-to-Live (e.g., 4 hours), the escalation engine promotes the request to the regional VP or notifies an executive on-call channel.
D. The Per-Seat Licensing Penalty#
Commercial SaaS pricing models penalize cross-functional collaboration.A General Counsel, Chief Compliance Officer, or Board Member might only need to review eight high-value transactions per fiscal quarter. Yet commercial CRM vendors demand an enterprise seat license (2,000 to 3,600 annually per user) simply to view the record and click "Approve."
A bespoke business portal decouples authentication from licensing. External stakeholders, legal advisors, and board members authenticate via corporate SAML SSO or secure one-time signed magic links, executing approvals at zero marginal software licensing cost.
2. Architecture Blueprint: Directed Acyclic Graph (DAG) State Machines#
To support complex business rules without accumulating spaghetti code, the approval engine must be modeled as a Directed Acyclic Graph (DAG) of states, guard conditions, and transition handlers.
Core Concepts of the DAG Engine:#
- Nodes (Approval Stages): Discrete phases of review (e.g.,
Sales_Director_Review,Legal_Compliance_Review,Executive_Board_Authorization). - Edges (Transitions): Permitted pathways between stages, governed by strict evaluation preconditions (guards).
- Quorum Types:
ALL(Unanimous): Every designated reviewer in the stage must approve before the stage resolves.ANY(First Responder): Any qualified actor within the authorized role can clear the stage.THRESHOLD(MofN): A minimum number of affirmative votes (e.g., 2 out of 3 Finance Directors) is required.
- Context Snapshotting: When an approval instance is triggered, the system freezes an immutable JSON snapshot of the underlying deal terms. If the sales rep subsequently attempts to alter deal values or discount percentages while the approval is pending, the transition engine detects the checksum drift and aborts the request.
3. Database Schema Design in PostgreSQL 16#
The database schema must separate the static workflow definition from the dynamic runtime instances, maintaining a tamper-evident audit log for SOC 2, SOX, and ISO 27001 regulatory compliance.
-- 1. Workflow Template Definitions
400 font-semibold">CREATE 400 font-semibold">TABLE approval_workflows (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
workflow_name VARCHAR(128) NOT NULL,
entity_type VARCHAR(64) NOT NULL, -- e.g., 400 font-semibold">class="text-emerald-300">'DEAL', 400 font-semibold">class="text-emerald-300">'PURCHASE_ORDER', 400 font-semibold">class="text-emerald-300">'CONTRACT'
is_active BOOLEAN NOT NULL DEFAULT 400">true,
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp()
);
-- 2. Workflow Stage Definitions (Configurable DAG Nodes)
400 font-semibold">CREATE TYPE quorum_policy AS ENUM (400 font-semibold">class="text-emerald-300">'ALL', 400 font-semibold">class="text-emerald-300">'ANY', 400 font-semibold">class="text-emerald-300">'THRESHOLD');
400 font-semibold">CREATE 400 font-semibold">TABLE approval_workflow_stages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
workflow_id UUID NOT NULL REFERENCES approval_workflows(id) ON 400 font-semibold">DELETE CASCADE,
stage_name VARCHAR(128) NOT NULL,
stage_order INT NOT NULL,
quorum_type quorum_policy NOT NULL DEFAULT 400 font-semibold">class="text-emerald-300">'ANY',
threshold_count INT DEFAULT 1,
sla_timeout_minutes INT NOT NULL DEFAULT 240, -- 4-hour SLA 400 font-semibold">default
-- Dynamic JSONB rules specifying when 400 font-semibold">this stage is triggered
-- e.g., {400 font-semibold">class="text-emerald-300">"discount_pct_gt": 15, 400 font-semibold">class="text-emerald-300">"contract_value_gte": 50000}
trigger_conditions JSONB NOT NULL DEFAULT 400 font-semibold">class="text-emerald-300">'{}'::jsonb,
required_role VARCHAR(64) NOT NULL, -- e.g., 400 font-semibold">class="text-emerald-300">'sales_director', 400 font-semibold">class="text-emerald-300">'finance_vp', 400 font-semibold">class="text-emerald-300">'legal_counsel'
escalation_role VARCHAR(64), -- e.g., 400 font-semibold">class="text-emerald-300">'cfo', 400 font-semibold">class="text-emerald-300">'cro'
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
UNIQUE (workflow_id, stage_order)
);
-- 3. Active Runtime Approval Instances
400 font-semibold">CREATE TYPE approval_overall_status AS ENUM (
400 font-semibold">class="text-emerald-300">'PENDING',
400 font-semibold">class="text-emerald-300">'IN_PROGRESS',
400 font-semibold">class="text-emerald-300">'APPROVED',
400 font-semibold">class="text-emerald-300">'REJECTED',
400 font-semibold">class="text-emerald-300">'CANCELLED'
);
400 font-semibold">CREATE 400 font-semibold">TABLE approval_instances (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
workflow_id UUID NOT NULL REFERENCES approval_workflows(id),
entity_id UUID NOT NULL,
requester_id UUID NOT NULL,
status approval_overall_status NOT NULL DEFAULT 400 font-semibold">class="text-emerald-300">'PENDING',
current_stage_order INT NOT NULL DEFAULT 1,
-- Frozen snapshot of entity data at the time of submission
entity_snapshot JSONB NOT NULL,
snapshot_checksum VARCHAR(64) NOT NULL, -- SHA-256 hash of entity_snapshot
started_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
resolved_at TIMESTAMPTZ
);
400 font-semibold">CREATE 400 font-semibold">INDEX idx_approval_instances_entity ON approval_instances (entity_id, status);
400 font-semibold">CREATE 400 font-semibold">INDEX idx_approval_instances_active ON approval_instances (current_stage_order) 400 font-semibold">WHERE status = 400 font-semibold">class="text-emerald-300">'IN_PROGRESS';
-- 4. Granular Individual Approver Actions (Immutable Ledger)
400 font-semibold">CREATE TYPE action_verdict AS ENUM (
400 font-semibold">class="text-emerald-300">'APPROVED',
400 font-semibold">class="text-emerald-300">'REJECTED',
400 font-semibold">class="text-emerald-300">'REQUESTED_CHANGES',
400 font-semibold">class="text-emerald-300">'DELEGATED',
400 font-semibold">class="text-emerald-300">'ESCALATED_SLA'
);
400 font-semibold">CREATE 400 font-semibold">TABLE approval_actions (
action_id BIGSERIAL PRIMARY KEY,
instance_id UUID NOT NULL REFERENCES approval_instances(id) ON 400 font-semibold">DELETE CASCADE,
stage_id UUID NOT NULL REFERENCES approval_workflow_stages(id),
actor_id UUID NOT NULL,
verdict action_verdict NOT NULL,
comment TEXT,
delegated_to_id UUID,
-- Cryptographic verification and client footprint
action_signature VARCHAR(128) NOT NULL, -- HMAC-SHA256 signature
ip_address INET NOT NULL,
user_agent TEXT,
recorded_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp()
);
400 font-semibold">CREATE 400 font-semibold">INDEX idx_approval_actions_instance ON approval_actions (instance_id, recorded_at DESC);
4. Production Code Implementation#
The following TypeScript implementation demonstrates an industrial-grade workflow orchestration engine. It enforces condition evaluation, consensus quorum convergence, and automated SLA timeout escalations.
A. Core Workflow Execution Engine (approval-engine.ts)#
400 font-semibold">import { createHash, createHmac } 400 font-semibold">from 400 font-semibold">class="text-emerald-300">'crypto';
400 font-semibold">import { Pool } 400 font-semibold">from 400 font-semibold">class="text-emerald-300">'pg';
400 font-semibold">export 400 font-semibold">interface WorkflowRuleContext {
discount_percentage: 400">number;
contract_value: 400">number;
payment_terms: 400">string;
territory: 400">string;
}
400 font-semibold">export 400 font-semibold">interface ReviewActionPayload {
instanceId: 400">string;
stageId: 400">string;
actorId: 400">string;
verdict: 400 font-semibold">class="text-emerald-300">'APPROVED' | 400 font-semibold">class="text-emerald-300">'REJECTED' | 400 font-semibold">class="text-emerald-300">'REQUESTED_CHANGES' | 400 font-semibold">class="text-emerald-300">'DELEGATED';
comment: 400">string;
delegatedToId?: 400">string;
clientIp: 400">string;
userAgent: 400">string;
}
400 font-semibold">export 400 font-semibold">class ApprovalWorkflowEngine {
constructor(
400 font-semibold">private 400 font-semibold">readonly db: Pool,
400 font-semibold">private 400 font-semibold">readonly hmacSecret: 400">string
) {}
/**
* Initializes a 400 font-semibold">new runtime approval instance against dynamic rules
*/
400 font-semibold">public 400 font-semibold">async submitForApproval(
workflowId: 400">string,
entityId: 400">string,
requesterId: 400">string,
entityData: WorkflowRuleContext
): 400">Promise<400">string> {
400 font-semibold">const client = 400 font-semibold">await 400 font-semibold">this.db.connect();
400 font-semibold">try {
400 font-semibold">await client.query(400 font-semibold">class="text-emerald-300">'BEGIN');
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// 1. Calculate tamper-evident SHA-256 checksum of payload
400 font-semibold">const snapshotString = JSON.stringify(entityData);
400 font-semibold">const checksum = createHash(400 font-semibold">class="text-emerald-300">'sha256').update(snapshotString).digest(400 font-semibold">class="text-emerald-300">'hex');
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// 2. Insert active instance
400 font-semibold">const insertInstanceSql = 400 font-semibold">class="text-emerald-300">`
400 font-semibold">INSERT INTO approval_instances
(workflow_id, entity_id, requester_id, status, current_stage_order, entity_snapshot, snapshot_checksum)
VALUES ($1, $2, $3, 'IN_PROGRESS', 1, $4::jsonb, $5)
RETURNING id;
`;
400 font-semibold">const res = 400 font-semibold">await client.query(insertInstanceSql, [
workflowId,
entityId,
requesterId,
snapshotString,
checksum,
]);
400 font-semibold">const instanceId = res.rows[0].id;
400 font-semibold">await client.query(400 font-semibold">class="text-emerald-300">'COMMIT');
400 font-semibold">return instanceId;
} 400 font-semibold">catch (err) {
400 font-semibold">await client.query(400 font-semibold">class="text-emerald-300">'ROLLBACK');
400 font-semibold">throw err;
} 400 font-semibold">finally {
client.release();
}
}
/**
* Processes an incoming approver vote and evaluates stage quorum
*/
400 font-semibold">public 400 font-semibold">async processReviewAction(payload: ReviewActionPayload): 400">Promise<{
stageResolved: 400">boolean;
workflowStatus: 400">string;
}> {
400 font-semibold">const client = 400 font-semibold">await 400 font-semibold">this.db.connect();
400 font-semibold">try {
400 font-semibold">await client.query(400 font-semibold">class="text-emerald-300">'BEGIN');
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// 1. Verify instance is still active
400 font-semibold">const instanceRes = 400 font-semibold">await client.query(
400 font-semibold">class="text-emerald-300">`400 font-semibold">SELECT id, status, current_stage_order, snapshot_checksum 400 font-semibold">FROM approval_instances 400 font-semibold">WHERE id = $1 FOR 400 font-semibold">UPDATE`,
[payload.instanceId]
);
400 font-semibold">if (instanceRes.rows.length === 0 || instanceRes.rows[0].status !== 400 font-semibold">class="text-emerald-300">'IN_PROGRESS') {
400 font-semibold">throw 400 font-semibold">new Error(400 font-semibold">class="text-emerald-300">'Approval instance is not in an actionable state.');
}
400 font-semibold">const instance = instanceRes.rows[0];
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// 2. Generate HMAC signature 400 font-semibold">for non-repudiation audit trail
400 font-semibold">const signatureData = 400 font-semibold">class="text-emerald-300">`${payload.instanceId}:${payload.actorId}:${payload.verdict}:${Date.now()}`;
400 font-semibold">const signature = createHmac(400 font-semibold">class="text-emerald-300">'sha256', 400 font-semibold">this.hmacSecret).update(signatureData).digest(400 font-semibold">class="text-emerald-300">'hex');
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// 3. 400">Record action in immutable audit log
400 font-semibold">await client.query(
400 font-semibold">class="text-emerald-300">`400 font-semibold">INSERT INTO approval_actions
(instance_id, stage_id, actor_id, verdict, comment, delegated_to_id, action_signature, ip_address, user_agent)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
[
payload.instanceId,
payload.stageId,
payload.actorId,
payload.verdict,
payload.comment,
payload.delegatedToId ?? 400">null,
signature,
payload.clientIp,
payload.userAgent,
]
);
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// 4. Handle immediate terminal verdicts
400 font-semibold">if (payload.verdict === 400 font-semibold">class="text-emerald-300">'REJECTED') {
400 font-semibold">await client.query(
400 font-semibold">class="text-emerald-300">`400 font-semibold">UPDATE approval_instances SET status = 'REJECTED', resolved_at = clock_timestamp() 400 font-semibold">WHERE id = $1`,
[payload.instanceId]
);
400 font-semibold">await client.query(400 font-semibold">class="text-emerald-300">'COMMIT');
400 font-semibold">return { stageResolved: 400">true, workflowStatus: 400 font-semibold">class="text-emerald-300">'REJECTED' };
}
400 font-semibold">if (payload.verdict === 400 font-semibold">class="text-emerald-300">'REQUESTED_CHANGES') {
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// Keeps instance in-progress but flags notification to requester
400 font-semibold">await client.query(400 font-semibold">class="text-emerald-300">'COMMIT');
400 font-semibold">return { stageResolved: 400">false, workflowStatus: 400 font-semibold">class="text-emerald-300">'CHANGES_REQUESTED' };
}
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// 5. Evaluate Quorum 400 font-semibold">for current stage
400 font-semibold">const stageRes = 400 font-semibold">await client.query(
400 font-semibold">class="text-emerald-300">`400 font-semibold">SELECT quorum_type, threshold_count 400 font-semibold">FROM approval_workflow_stages 400 font-semibold">WHERE id = $1`,
[payload.stageId]
);
400 font-semibold">const stage = stageRes.rows[0];
400 font-semibold">const votesRes = 400 font-semibold">await client.query(
400 font-semibold">class="text-emerald-300">`400 font-semibold">SELECT COUNT(*) as affirmative_votes
400 font-semibold">FROM approval_actions
400 font-semibold">WHERE instance_id = $1 AND stage_id = $2 AND verdict = 'APPROVED'`,
[payload.instanceId, payload.stageId]
);
400 font-semibold">const affirmativeVotes = parseInt(votesRes.rows[0].affirmative_votes, 10);
400 font-semibold">let stagePassed = 400">false;
400 font-semibold">if (stage.quorum_type === 400 font-semibold">class="text-emerald-300">'ANY' && affirmativeVotes >= 1) {
stagePassed = 400">true;
} 400 font-semibold">else 400 font-semibold">if (stage.quorum_type === 400 font-semibold">class="text-emerald-300">'THRESHOLD' && affirmativeVotes >= stage.threshold_count) {
stagePassed = 400">true;
}
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// 6. Transition to next stage or resolve workflow
400 font-semibold">let newWorkflowStatus = 400 font-semibold">class="text-emerald-300">'IN_PROGRESS';
400 font-semibold">if (stagePassed) {
400 font-semibold">const nextStageRes = 400 font-semibold">await client.query(
400 font-semibold">class="text-emerald-300">`400 font-semibold">SELECT id, stage_order 400 font-semibold">FROM approval_workflow_stages
400 font-semibold">WHERE workflow_id = (400 font-semibold">SELECT workflow_id 400 font-semibold">FROM approval_instances 400 font-semibold">WHERE id = $1)
AND stage_order > $2
400 font-semibold">ORDER BY stage_order ASC LIMIT 1`,
[payload.instanceId, instance.current_stage_order]
);
400 font-semibold">if (nextStageRes.rows.length > 0) {
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// Advance to next stage
400 font-semibold">await client.query(
400 font-semibold">class="text-emerald-300">`400 font-semibold">UPDATE approval_instances SET current_stage_order = $1 400 font-semibold">WHERE id = $2`,
[nextStageRes.rows[0].stage_order, payload.instanceId]
);
} 400 font-semibold">else {
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// Terminal approval reached
newWorkflowStatus = 400 font-semibold">class="text-emerald-300">'APPROVED';
400 font-semibold">await client.query(
400 font-semibold">class="text-emerald-300">`400 font-semibold">UPDATE approval_instances SET status = 'APPROVED', resolved_at = clock_timestamp() 400 font-semibold">WHERE id = $1`,
[payload.instanceId]
);
}
}
400 font-semibold">await client.query(400 font-semibold">class="text-emerald-300">'COMMIT');
400 font-semibold">return { stageResolved: stagePassed, workflowStatus: newWorkflowStatus };
} 400 font-semibold">catch (err) {
400 font-semibold">await client.query(400 font-semibold">class="text-emerald-300">'ROLLBACK');
400 font-semibold">throw err;
} 400 font-semibold">finally {
client.release();
}
}
}
B. Automated SLA Escalation Watchdog Worker#
Deals must never stall due to absent decision-makers. This worker scans active approval stages against their configured SLA timeouts, triggering omnichannel escalation alerts before quarter-end deadlines slip.
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// workers/sla-escalation-worker.ts
400 font-semibold">import { Pool } 400 font-semibold">from 400 font-semibold">class="text-emerald-300">'pg';
400 font-semibold">import { Redis } 400 font-semibold">from 400 font-semibold">class="text-emerald-300">'ioredis';
400 font-semibold">export 400 font-semibold">class SLAEscalationWatchdog {
constructor(
400 font-semibold">private 400 font-semibold">readonly db: Pool,
400 font-semibold">private 400 font-semibold">readonly redis: Redis
) {}
400 font-semibold">public 400 font-semibold">async evaluateExpiredSLAs(): 400">Promise<400">number> {
400 font-semibold">const expiredQuery = 400 font-semibold">class="text-emerald-300">`
400 font-semibold">SELECT
i.id as instance_id,
i.entity_id,
s.id as stage_id,
s.stage_name,
s.escalation_role,
s.sla_timeout_minutes,
i.started_at,
EXTRACT(EPOCH 400 font-semibold">FROM (clock_timestamp() - i.started_at))/60 as elapsed_minutes
400 font-semibold">FROM approval_instances i
400 font-semibold">JOIN approval_workflow_stages s
ON s.workflow_id = i.workflow_id AND s.stage_order = i.current_stage_order
400 font-semibold">WHERE i.status = 'IN_PROGRESS'
AND EXTRACT(EPOCH 400 font-semibold">FROM (clock_timestamp() - i.started_at))/60 > s.sla_timeout_minutes
AND NOT EXISTS (
400 font-semibold">SELECT 1 400 font-semibold">FROM approval_actions a
400 font-semibold">WHERE a.instance_id = i.id
AND a.stage_id = s.id
AND a.verdict = 'ESCALATED_SLA'
);
`;
400 font-semibold">const res = 400 font-semibold">await 400 font-semibold">this.db.query(expiredQuery);
400 font-semibold">let escalatedCount = 0;
400 font-semibold">for (400 font-semibold">const row of res.rows) {
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// 1. Mark SLA escalation in immutable ledger
400 font-semibold">await 400 font-semibold">this.db.query(
400 font-semibold">class="text-emerald-300">`400 font-semibold">INSERT INTO approval_actions
(instance_id, stage_id, actor_id, verdict, comment, action_signature, ip_address)
VALUES ($1, $2, '00000000-0000-0000-0000-000000000000', 'ESCALATED_SLA',
'Automated SLA Breach: Escalated to ' || $3, 'SYSTEM_AUTO_ESCALATION', '127.0.0.1')`,
[row.instance_id, row.stage_id, row.escalation_role]
);
400 font-semibold">class=400 font-semibold">class="text-emerald-300">"text-slate-500 italic">// 2. Dispatch high-priority alert to Redis Queue 400 font-semibold">for Slack Block Kit & SMS dispatch
400 font-semibold">await 400 font-semibold">this.redis.lpush(
400 font-semibold">class="text-emerald-300">'queue:notifications:urgent',
JSON.stringify({
400 font-semibold">type: 400 font-semibold">class="text-emerald-300">'SLA_BREACH_ESCALATION',
instanceId: row.instance_id,
entityId: row.entity_id,
stageName: row.stage_name,
targetRole: row.escalation_role,
elapsedMinutes: Math.round(row.elapsed_minutes),
})
);
escalatedCount++;
}
400 font-semibold">return escalatedCount;
}
}
5. Omnichannel Approvals: Slack & Signed Magic Links#
High-velocity executives do not want to log into an administrative dashboard to approve an urgent discount. They work inside Slack, Microsoft Teams, and mobile email clients.
A bespoke portal surfaces approvals wherever decision-makers live without compromising security:
[Omnichannel Approval Channels]
│
├──► 1. Slack Interactive Block Kit:
│ Renders contract value, gross margin %, and discount directly in a 400 font-semibold">private Slack DM.
│ 400 font-semibold">class="text-emerald-300">"Approve" and 400 font-semibold">class="text-emerald-300">"Request Changes" buttons trigger an encrypted backend webhook in < 400ms.
│
├──► 2. Signed Time-Limited Magic Links:
│ Executive receives a mobile email with a single-use JWT URL.
│ Clicking opens a zero-login mobile sheet with Face ID / biometric authentication.
│
└──► 3. Web Portal Executive Hub:
Full desktop audit 400 font-semibold">interface with side-by-side contract diffing and margin sensitivity sliders.
6. Regulatory Compliance & Segregation of Duties (SoD)#
For public enterprises and regulated financial institutions, approval systems are subjected to rigorous annual audits under Sarbanes-Oxley (SOX) Section 404 and SOC 2 Type II (Trust Services Criteria).
A custom approval architecture satisfies enterprise compliance auditors out-of-the-box:
- Strict Segregation of Duties (SoD): The workflow engine mathematically prevents a user from approving their own submission. Even if a Regional Vice President creates a deal record, the system automatically excludes them from the reviewer pool for that instance.
- Cryptographic Non-Repudiation: Every approval action is hashed with HMAC-SHA256, capturing the actor's UUID, verified corporate email, IP address, user-agent string, and timestamp.
- Partitioned Historical Preservation: Because PostgreSQL tables are partitioned by calendar quarter, historical audit records from previous fiscal years can be placed in read-only tablespaces or archived to WORM (Write Once, Read Many) cloud storage for statutory seven-year retention periods.
7. Operational Outcomes: Commercial CRM vs. Custom Portal#
[Visual Asset: Operational Metrics Comparison - Commercial CRM Workflow vs. Custom Portal DAG Engine]
+--------------------------------------+--------------------------------+---------------------------------+
| OPERATIONAL METRIC | COMMERCIAL CRM WORKFLOW | BESPOKE DAG PORTAL ENGINE |
+--------------------------------------+--------------------------------+---------------------------------+
| Average Approval Turnaround Time | 3.8 Business Days | 4.2 Hours (88% Acceleration) |
| Redline Handling on Contract Terms | Full Workflow Restart (Reset) | Targeted Node Remediation |
| Multi-Stakeholder Quorum Support | Sequential Waterfall Only | Native Parallel DAG Execution |
| SLA Timeout & Auto-Escalation | Fragile Batch Jobs (Daily) | Real-Time Event Streams (<10ms) |
| Non-Licensed Reviewer Support | $165 - $300 / user / month | $0 (Unlimited SAML / SSO Users) |
| Compliance Audit Readiness | Surface-Level Field Edits | Cryptographic HMAC Audit Log |
+--------------------------------------+--------------------------------+---------------------------------+
8. Frequently Asked Questions#
1. How does a custom approval engine handle schema changes when new stages are added?#
In our PostgreSQL architecture, workflow stages are stored as relational data (approval_workflow_stages), not hardcoded application logic. Introducing a new approval tier (e.g., adding an Information Security review for deals over $100k) requires inserting a single row into the stage configuration table. Active in-flight approvals maintain their original state snapshot without disruption.2. Can external stakeholders (like outside legal counsel) approve requests without portal credentials?#
Yes. The engine generates cryptographically signed, short-lived (e.g., 24-hour) JSON Web Tokens (JWT) embedded in secure action links. When external counsel clicks the link, the token verifies their authorization, displays the specific contract clause requiring review, and records their signed verdict into the audit ledger without granting access to the broader internal database.3. What prevents an approver from approving a deal if terms change during the review?#
When an approval request is initiated, the engine computes a cryptographic SHA-256 hash of the complete entity payload (snapshot_checksum). If an account executive alters the discount percentage, payment terms, or product scope while a review is underway, the engine detects the checksum mismatch and automatically pauses the workflow, alerting reviewers to the unauthorized modification.4. How difficult is it to migrate active approvals from an existing commercial CRM?#
The migration employs an asynchronous event bridge. New deals route immediately through the custom portal's DAG engine, while legacy in-flight approvals continue to run in parallel until resolved. A Change Data Capture (CDC) worker syncs final status updates back to the legacy system to keep executive dashboards unified during the multi-week cutover.5. How does this architecture prevent deadlocks in parallel approval stages?#
A Directed Acyclic Graph is topologically sorted during compilation. The engine's validation pipeline checks the workflow graph at creation time to mathematically prove that no circular dependencies (e.g., Stage A waiting on Stage B, while Stage B waits on Stage A) can exist. In runtime execution, timeouts and default fallback roles ensure that no transaction remains trapped indefinitely.Engineer High-Velocity Approval Portals with KNetwork#
Sluggish, inflexible approval workflows are an invisible drag on enterprise revenue. Whether your organization is losing deals to slow contract turnaround times, paying tens of thousands of dollars for unnecessary CRM licenses, or struggling to satisfy strict SOX/SOC 2 compliance audits, KNetwork’s principal software architects design and deliver bespoke portals tailored to your company's exact operational logic.
Explore our Custom CRM & Business Portals and Custom Software Engineering capabilities, or Book an Architecture Discovery Call with our engineering leadership to review your workflow automation architecture today.
Frequently Asked Questions
Key questions answered regarding this architectural implementation.
Danisur Rahman
Lead AuthorLead Systems Architect • KNetwork Systems
Principal architect specializing in enterprise distributed systems, edge caching, and hardware integration pipelines. Leads engineering audits, high-concurrency database optimizations, and zero-trust VPC deployments across high-growth ventures.
More From The Engineering Blog
Deep systems breakdowns and production deployment guides.
Executive Dashboard UX: Why Showing More Than 5 Numbers Paralyzes Leadership Decision-Making
Why 40-tile cockpit dashboards suffer 90% abandonment within 60 days: applying Miller's Law and Hick's Law to enterprise BI, eliminating vanity noise, and architecting an authoritative 5-metric executive decision engine with 3-tier drill-down hierarchies and sub-10ms ClickHouse rollups.
Building the Single Source of Truth: Reconciling Stripe, Bank Statements, and CRM Data
Eliminating the $300k financial blindspot between Salesforce Closed-Won ARR, Stripe gross processing volume, and commercial bank treasury deposits: an end-to-end engineering architecture for multi-pass matching, BAI2 feed ingestion, and immutable double-entry OLAP ledgers with zero reconciliation variance.
Enjoyed this technical breakdown?
Subscribe to receive new architectural guides, system teardowns, and engineering benchmarks directly in your inbox.