Skip to content

Routing, Roles, and Assignment

This page is the clearest reference for how tickets move through the system, who can do what, how departments are chosen, and when an agent is auto-assigned.

Routing logic

The API computes routing in apps/api/src/lib/router.ts using shared thresholds from @arabic-itsm/shared.

Confidence thresholds

ModeRuleTicket status
autoevery available classification level is >= 0.85open
reviewnot auto, but l1.confidence >= 0.60pending_review
manualbelow review threshold, classifier failed, or classifier unavailablenew

Important detail

The stored routing_confidence is the minimum confidence across:

  • l1
  • l2
  • optional l3

That means one weak level can prevent auto-routing even if the others are high.

What each routing mode actually means

auto

  • classifier confidence is high enough across the available levels
  • system opens the ticket immediately
  • department is chosen from the L1 label
  • ticket is auto-assigned to the least-loaded agent in that department
  • assigned agent receives an automatic assignment notification

review

  • classifier has enough confidence to suggest direction, but not enough for full automatic handling
  • status is set to pending_review
  • ticket is routed to Service Desk if that department exists
  • otherwise it stays with the matched department
  • no direct agent auto-assignment is made
  • review/triage agents are notified to inspect and confirm

manual

  • classifier confidence is too low, or classifier output is unavailable
  • status is set to new
  • ticket is not auto-assigned
  • admins are notified for manual triage
  • the helpdesk still works even when classification fails

Department routing

Department selection is based on the ticket's classification.l1.label.

The API matches that label against departments.l1_labels.

Expected department families

L1 labelExpected department
AccessAccess Management
HardwareHardware Support
NetworkNetwork Operations
SecuritySecurity Team
ServiceService Desk
SoftwareSoftware Support

Assignment logic

Assignment is handled in apps/api/src/lib/assignment.ts.

For auto tickets

  1. Find the department by L1 label.
  2. Query all agents in that department.
  3. Count each agent's open tickets with statuses:
    • new
    • pending_review
    • open
    • in_progress
  4. Choose the least-loaded agent.
  5. Save assigned_agent_id.
  6. Record an audit entry.
  7. Notify the assigned agent.

For review tickets

  1. Find the department suggested by L1.
  2. Prefer the Service Desk department for triage if it exists.
  3. Set department_id to that triage department.
  4. Do not auto-assign an individual agent.
  5. Notify the triage agents.

For manual tickets

  • no automatic assignment
  • no confidence-based department workflow
  • admins are expected to step in

Roles and access model

Role information comes from Supabase user app_metadata and is enforced by the API.

end_user

  • can create tickets
  • can read only their own tickets
  • can see their own ticket comments and notifications
  • cannot update workflow fields or routing

agent

  • can read tickets in their department
  • can also read tickets directly assigned to them
  • can update ticket status, priority, department, and assignee when allowed
  • can override routing
  • can work comments, queues, and review flows

admin

  • can read and manage all tickets
  • can manage users and invitations
  • can access stats utilities and developer tools
  • can reseed demo data and quick actions

Human override behavior

Routing override

When an agent or admin changes routing through PATCH /api/v1/tickets/:id/routing:

  • routing_overridden is forced to true
  • an audit entry is written
  • notifications are sent
  • the override becomes measurable in stats

Reclassify

When an agent re-runs AI classification:

  • classification and routing_confidence are refreshed
  • if the ticket was already manually overridden, the system does not silently replace the human decision

Failure behavior

If the classifier is unavailable:

  • ticket creation still succeeds
  • classification = null
  • routing_mode = manual
  • status = new
  • routing_reason = "Classifier unavailable."

This fallback is intentional. The helpdesk is not dependent on the classifier being online.

Built from the current monorepo implementation.