Skip to Content
WorkflowsNodesHITL Router Node

HITL Router Node

Route tasks to specific human reviewers based on criteria like expertise, workload, or priority.

Overview

The HITL Router Node assigns review tasks to specific reviewers based on configurable routing rules. It enables:

  • Expert routing - Send complex cases to specialized reviewers
  • Workload balancing - Distribute tasks evenly across available reviewers
  • Priority escalation - Route urgent tasks to senior reviewers
  • Team assignment - Route by document type, customer, or region
  • SLA management - Ensure high-priority tasks meet deadlines

Unlike Approval or Correction nodes (which define task structure), the Router focuses on assignment logic.

When to Use

Use a HITL Router Node when you need:

  • Specialized expertise - Medical claims to medical reviewers, legal docs to attorneys
  • Dynamic assignment - Route based on content, metadata, or real-time availability
  • Workload balancing - Prevent reviewer overload by distributing evenly
  • Escalation logic - Route to senior reviewers if automated confidence is low
  • Multi-tier review - First-pass review by juniors, second-pass by seniors

For simple fixed assignment, configure reviewers directly in Approval/Correction nodes.

Configuration

Routing Rules

Define how tasks are assigned:

{ "routing_rules": [ { "condition": "$.nodes.classify.output.category == 'medical'", "reviewers": ["role:medical_reviewer"], "priority": "high" }, { "condition": "$.nodes.classify.output.category == 'legal'", "reviewers": ["role:legal_reviewer"], "priority": "high" }, { "condition": "$.data.amount > 10000", "reviewers": ["role:senior_reviewer"], "priority": "urgent" }, { "condition": "default", "reviewers": ["team:general_review"], "priority": "normal" } ] }

Rules are evaluated in order. First matching rule determines assignment.

Assignment Strategies

Control how reviewers are selected:

StrategyDescriptionUse Case
round_robinAssign to reviewers in rotationEqual distribution
load_balancedAssign to reviewer with fewest pending tasksPrevent overload
randomRandom selection from eligible reviewersFair distribution without patterns
priority_basedAssign to highest-priority reviewer availableExpert routing
specific_userAssign to named userVIP customer or special case
{ "assignment_strategy": "load_balanced", "fallback_strategy": "round_robin" }

If no eligible reviewers for primary strategy, use fallback.

Reviewer Pools

Define pools of reviewers with metadata:

{ "reviewer_pools": { "medical_experts": { "members": ["user_123", "user_456", "user_789"], "expertise": ["cardiology", "radiology", "oncology"], "max_concurrent_tasks": 10 }, "legal_team": { "members": ["user_abc", "user_def"], "expertise": ["contracts", "compliance"], "max_concurrent_tasks": 5 }, "general_review": { "members": ["user_111", "user_222", "user_333", "user_444"], "max_concurrent_tasks": 20 } } }

Routing rules reference pools by name.

Priority Levels

Assign priority to tasks based on routing criteria:

PrioritySLANotificationUse Case
urgent1 hourImmediate SMS/emailSystem outages, VIP customers
high4 hoursEmail + in-appHigh-value transactions, compliance
normal24 hoursIn-app onlyStandard operations
low7 daysBatch emailTraining data, non-critical review
{ "priority_mapping": [ { "condition": "$.data.customer_tier == 'vip'", "priority": "urgent" }, { "condition": "$.nodes.extract.output.amount > 50000", "priority": "high" }, { "condition": "default", "priority": "normal" } ] }

Routing Examples

Route by Document Type

{ "routing_rules": [ { "condition": "$.nodes.classify.output.doc_type == 'invoice'", "reviewers": ["team:accounts_payable"], "assignment_strategy": "load_balanced" }, { "condition": "$.nodes.classify.output.doc_type == 'contract'", "reviewers": ["team:legal"], "assignment_strategy": "round_robin" }, { "condition": "$.nodes.classify.output.doc_type == 'medical_claim'", "reviewers": ["pool:medical_experts"], "assignment_strategy": "priority_based", "priority": "high" } ] }

Route by Confidence Score

Route low-confidence extractions to expert reviewers:

{ "routing_rules": [ { "condition": "$.nodes.extract.output.confidence < 0.7", "reviewers": ["role:expert_reviewer"], "priority": "high", "comment": "Low confidence extraction, requires expert review" }, { "condition": "$.nodes.extract.output.confidence >= 0.7 && $.nodes.extract.output.confidence < 0.9", "reviewers": ["team:general_review"], "priority": "normal" }, { "condition": "$.nodes.extract.output.confidence >= 0.9", "reviewers": ["role:quality_assurance"], "priority": "low", "assignment_strategy": "random", "sample_rate": 0.1 } ] }

The last rule samples 10% of high-confidence extractions for spot-checking.

Route by Customer Tier

{ "routing_rules": [ { "condition": "$.data.customer.tier == 'enterprise'", "reviewers": ["user_senior_account_manager"], "assignment_strategy": "specific_user", "priority": "urgent", "sla_hours": 1 }, { "condition": "$.data.customer.tier == 'business'", "reviewers": ["team:account_managers"], "assignment_strategy": "load_balanced", "priority": "high", "sla_hours": 4 }, { "condition": "default", "reviewers": ["team:support"], "assignment_strategy": "round_robin", "priority": "normal", "sla_hours": 24 } ] }

Route by Region and Language

{ "routing_rules": [ { "condition": "$.data.region == 'EMEA' && $.nodes.detect_language.output.language == 'de'", "reviewers": ["team:emea_de"], "priority": "normal" }, { "condition": "$.data.region == 'EMEA' && $.nodes.detect_language.output.language == 'fr'", "reviewers": ["team:emea_fr"], "priority": "normal" }, { "condition": "$.data.region == 'AMER'", "reviewers": ["team:americas"], "priority": "normal" } ] }

Advanced Features

Cascading Routing

Route through multiple tiers:

{ "tier_1": { "reviewers": ["team:junior_reviewers"], "assignment_strategy": "load_balanced", "timeout_hours": 24, "on_timeout": "escalate_to_tier_2" }, "tier_2": { "reviewers": ["team:senior_reviewers"], "assignment_strategy": "round_robin", "timeout_hours": 12, "on_timeout": "escalate_to_tier_3" }, "tier_3": { "reviewers": ["role:manager"], "assignment_strategy": "specific_user", "timeout_hours": 6, "on_timeout": "alert_leadership" } }

Tasks auto-escalate if not completed within SLA.

Reviewer Availability

Consider reviewer availability and working hours:

{ "availability_rules": { "respect_working_hours": true, "working_hours": { "timezone": "America/New_York", "start": "09:00", "end": "17:00", "days": ["monday", "tuesday", "wednesday", "thursday", "friday"] }, "out_of_hours_fallback": { "reviewers": ["team:on_call"], "priority": "urgent" } } }

Tasks assigned outside working hours route to on-call team.

Skill-Based Routing

Match reviewer expertise to document requirements:

{ "skill_matching": { "required_skills": ["$.nodes.classify.output.required_expertise"], "reviewer_skills": { "user_123": ["cardiology", "radiology"], "user_456": ["oncology", "pathology"], "user_789": ["general_medicine"] }, "fallback_if_no_match": { "reviewers": ["role:generalist"], "add_note": "No specialist available, assigned to generalist" } } }

Routes to reviewer with matching skill, falls back to generalist if none available.

Output

The HITL Router Node produces assignment metadata:

{ "routing_result": { "assigned_reviewer_id": "user_123", "assigned_reviewer_email": "jane.doe@company.com", "assignment_strategy": "load_balanced", "priority": "high", "sla_deadline": "2026-03-19T18:30:00Z", "routing_reason": "Medical expertise match: cardiology", "selected_path_id": "assigned" } }

Access via JSONPath:

  • $.nodes.router.output.routing_result.assigned_reviewer_id
  • $.nodes.router.output.routing_result.priority

Workload Management

Capacity Limits

Prevent reviewer overload:

{ "capacity_limits": { "user_123": { "max_concurrent_tasks": 10, "max_daily_tasks": 50 }, "team:general_review": { "max_concurrent_tasks_per_member": 15, "max_daily_tasks_per_member": 75 } }, "overflow_action": { "type": "queue", "notify_manager": true } }

If all reviewers at capacity, tasks queue and manager is notified.

Balancing Metrics

Track load balancing effectiveness:

MetricDescriptionTarget
Task distribution varianceEvenness of task distribution< 10% variance
Average wait timeTime from assignment to first view< 30 minutes
SLA compliance% tasks completed within SLA> 95%
Reviewer utilization% of capacity used70-90%

View in OPERATEAnalyticsReviewer Workload.

Notifications

Notify reviewers when tasks are assigned:

{ "notifications": { "on_assignment": { "email": true, "in_app": true, "sms": false }, "urgent_assignments": { "email": true, "in_app": true, "sms": true, "phone_call": true }, "approaching_sla": { "hours_before_deadline": [2, 0.5], "email": true, "in_app": true } } }

Urgent tasks trigger SMS/call for immediate attention.

Best Practices

  1. Define clear routing rules - Use explicit conditions, avoid ambiguous logic
  2. Test routing logic - Verify assignments with test scenarios before production
  3. Monitor workload balance - Alert if variance exceeds threshold
  4. Set realistic SLAs - Match reviewer capacity and working hours
  5. Provide routing context - Add comments explaining why task was routed
  6. Handle edge cases - Always have default/fallback routing rule
  7. Respect availability - Don’t assign during off-hours unless urgent
  8. Track metrics - Monitor SLA compliance and distribution fairness

Routing logic runs synchronously at workflow execution time. Complex routing with many rules may add latency.

Integration with Review Systems

HITL Router integrates with external review systems:

{ "external_integration": { "type": "webhook", "endpoint": "https://review-system.company.com/api/tasks", "headers": { "Authorization": "Bearer {{gateway_token}}" }, "payload": { "task_id": "$.run_id", "assigned_to": "$.nodes.router.output.routing_result.assigned_reviewer_email", "priority": "$.nodes.router.output.routing_result.priority", "data": "$.nodes.extract.output" } } }

Creates tasks in external systems with routing metadata.

API Access

Programmatically manage routing:

// Get routing stats const stats = await trpc.routing.getStats.query({ time_range: 'last_24h' }); // Override routing for specific task await trpc.routing.override.mutate({ task_id: 'task_abc123', new_reviewer_id: 'user_xyz', reason: 'Original reviewer unavailable' }); // Get reviewer workload const workload = await trpc.routing.getReviewerWorkload.query({ reviewer_id: 'user_123' });
Last updated on