Skip to content

Escalation — Routing & Team Ownership

Purpose

This page explains how Escalation Routing works in HUPH: the cluster ownership model, when escalations fire, how notifications are routed to the right counselor, and how to reassign clusters. Intended for counselors, marketing staff, and admins managing team allocation.

Prerequisites

  • Logged in with the appropriate role (cluster counselor, marketing_staff, marketing_admin, or admin)
  • Understand Leads pipeline and Inbox

The 5 Clusters and Counselor Teams

HUPH organizes UPH's programs into 5 academic clusters:

Cluster Code Cluster Name
CASS College of Arts & Social Sciences
CBT College of Business & Technology
CHS College of Health Sciences
CIST College of Information Science & Technology
CNE College of Nursing & Education

Each cluster has counselors with role marketing_counselor and their own cluster code. Besides per-cluster counselors there are two global roles: marketing_staff and marketing_admin who receive all notifications.

Ownership Model (Hybrid-lite)

Every lead has a cluster_id and an owner_source that determines who "owns" the lead. Three owner sources:

owner_source Meaning Priority
program_match Auto — from the program the user mentioned Weakest (fluid)
register_intent Auto — from a "want_register" intent in chat Strong (locks)
manual Admin override via UI Strongest (always wins)

Truth table:

  • New lead arrives → cluster is resolved from program_interestowner_source = program_match. If the user switches programs in a later message, the cluster moves automatically.
  • If a want_register intent is detected + contact is captured → owner_source upgrades to register_intent, the cluster is locked (owner_locked_at = NOW()). Future program_match changes no longer flip the cluster.
  • Admin clicks Reassign in the UI → owner_source = manual + lock. This is final — only another admin can change it.

When Escalation Fires

There are 3 seeded escalation rules (more can be added at /admin/settings/escalation-rules):

  1. Frustrated user → counselor — fires when sentiment = frustrated (priority: high)
  2. Long conversation no resolution — fires when message count > 15 and stage still inquiry (priority: medium)
  3. Hot lead notification — fires when lead_score ≥ 80 (notify-only, doesn't escalate)

Rules 1 and 2: action = escalateconversations.status flips to escalated, bot pauses, notifications go to the cluster counselor + global recipients.

Rule 3: action = notify → status does NOT change, only a notification fires (bot keeps running).

Two Notification Kinds

Kind Title When
escalation "Escalation: " Escalate rule fires
notify "Hot lead: " Notify rule fires

Steps

1. See incoming notifications

Notifications appear on the bell icon in the admin header. Click to open the panel showing escalations + hot leads. New rows appear in realtime via Socket.io (without refresh).

 ┌─────────────────────────────────────────────┐
 │  🔔 3 new                                    │
 │                                              │
 │  ⚠ Escalation: Request human help            │
 │    Cluster CBT • 2 minutes ago               │
 │                                              │
 │  🔥 Hot lead: Rudi H.                        │
 │    Score 85 — Informatics • 5m ago           │
 │                                              │
 │  ⚠ Escalation: Complaint                     │
 │    Cluster CASS • 15m ago                    │
 └─────────────────────────────────────────────┘

2. Notification scope per role

Who gets what?

  • marketing_counselor (cluster X) → only escalations for cluster X + hot leads for cluster X
  • marketing_staff → all escalations and hot leads (global)
  • marketing_admin → all (global)

If cluster_id is NULL (legacy/unresolvable lead), notifications go only to marketing_staff + marketing_admin.

3. Reassign cluster from lead detail

Sometimes a counselor from cluster A realizes a lead should belong to cluster B (e.g. user said "interested in Business" but the resolver mis-matched). From the lead detail page /admin/leads-v2/[id]:

  • See the cluster badge + owner_source label in the header ("Auto via program" / "Auto via register" / "Manual by admin")
  • Click the Reassign dropdown → pick the new cluster (5 clusters
  • "Unassign")
  • A ConfirmDialog appears ("Are you sure?") → confirm
  • owner_source becomes manual, owner_locked_at=NOW — final lock

The change propagates to conversations.cluster_id via trigger — the associated conversation immediately moves to the new cluster's Socket.io room.

4. Filter leads by cluster

In /admin/leads-v2, the first filter chip row shows cluster counts: CASS(10) CBT(11) CHS(4) CIST(0) CNE(0) unassigned(3). Click one to filter. Counselors from cluster X see the same badge counts but only get access to cluster X (after Phase 1.5 RBAC is active).

5. Counselor dashboard view

Each counselor has a /admin/counselor-dashboard page (Phase 1.6) that shows:

  • Only leads from that counselor's cluster
  • Pending escalation queue that needs action
  • Counselor KPIs: conversion rate, response time, leads contacted
  • Shortcut to the inbox filtered by cluster

Example scenarios

User chats "I want to register" — cluster locked. Rudi chats via WhatsApp, initially asks about Animation (CASS), then provides name+phone and says "I want to register". Flow: pre-pass upsert → cluster CASS + program_match. Post-dispatch register_intent detected + contact captured → second upsert → cluster stays CASS but owner_source upgrades to register_intent, locked. If Rudi later mentions a CBT program, the cluster won't flip.

Admin reassigns from CBT to CHS. A marketing admin sees on a lead detail that "Budi Santoso" is in cluster CBT because he mentioned "Business", but his real intent is "Medicine" (CHS). Admin clicks Reassign → CHS → confirm. The lead is now CHS + manual. The CHS counselor gets a new notification in their queue; the CBT counselor no longer has access.

Troubleshooting

Counselor doesn't get a notification even though the lead is in their cluster. Symptom: CBT lead escalated but the CBT counselor doesn't see the bell icon update. Cause: conversations.cluster_id is NULL (propagation trigger didn't run), or the counselor wasn't logged in when the event fired. Fix: open the lead detail from the list → verify the cluster badge → if NULL, reassign manually. If it's correct, refresh the dashboard.

Wrong cluster assignment for a clearly-matched program. Symptom: user mentions "Informatics" but ends up in CASS (should be CIST). Cause: incomplete cluster_programs mapping — program name didn't match exactly or by prefix. Fix: admin reassign manually; report to the dev team to update the cluster_programs table.

Duplicate notifications from 2 rule fires. Symptom: user frustrated + long conversation → 2 notifications for the same event. Cause: dedup not yet implemented. Fix: ignore the duplicate; dedup is being considered for a later phase.

"Unauthorized" when opening a lead from another cluster. Symptom: CBT counselor clicks a CASS lead link in a notification → 403. Cause: RBAC Phase 1.5 is active — counselors can only access their own cluster. Fix: contact an admin for assignment or reassignment.

See also