[object Object]

A high-intent enterprise lead lands in the chat and goes to a rep who handles SMB self-serve. The rep does their best, the buyer disengages, and the deal never gets logged. The Conversations inbox routed correctly per the default rules — the rules were the wrong ones. Routing has more knobs than most teams use.

The four routing dimensions you actually need

  • Skill: who handles enterprise vs SMB, EMEA vs Americas
  • Channel: chat behaves differently from email and forms
  • Load: capacity per rep right now
  • Hours: rep online and within shift

Default round-robin handles only one of these. Build an automation layer for the rest.

Map skills to inbox properties

Add a skill_tags property on the user record (custom user property via Operations Hub). Possible values: enterprise_sales, smb_sales, tier1_support, tier2_support, billing, emea, apac. Routing logic reads tags and matches to a routing input on the conversation:

Routing input on conversation:
  intent: enterprise_sales
  region: emea
  language: en

Eligible reps =
  user.skill_tags includes "enterprise_sales"
  AND user.skill_tags includes "emea"
  AND user.online_status = available
  AND user.current_chats < user.max_concurrent

Load-aware round-robin

A rep with 3 active chats and a rep with 0 should not get equal odds. Build a custom routing action via a workflow + custom code:

// Custom code action in workflow
const eligible = await getEligibleReps(routingInput);
if (eligible.length === 0) return { outputFields: { rep: null } };
eligible.sort((a, b) => {
  const loadA = a.current_chats / a.max_concurrent;
  const loadB = b.current_chats / b.max_concurrent;
  if (loadA !== loadB) return loadA - loadB;
  return a.last_assigned_at - b.last_assigned_at; // older first
});
const winner = eligible[0];
await assignConversation(conversationId, winner.id);
return { outputFields: { rep: winner.id } };

Detect intent before routing

The first chat message is your signal. Run a fast classification step before routing decides where it goes:

Pattern -> Intent
"pricing", "quote", "demo"            -> sales
"login", "password", "error", "bug"   -> support
"invoice", "billing", "subscription"  -> billing
"partner", "reseller"                 -> partnerships
default                                -> sales (or your fallback)

For higher accuracy, call a Breeze intent classifier or a small LLM with a fixed prompt. Store the intent on the conversation property for analytics later.

Office-hours fallback

A 2 a.m. enterprise chat with no rep online needs a graceful fallback: capture intent, route to email queue, set expectations. The worst pattern is a chat widget that promises live help and then sits unanswered:

{% if not has_online_rep %}
  <p>Our team is offline right now. Drop your question and email and 
     we'll respond within 2 business hours.</p>
{% endif %}

Handoffs without losing context

A chat starting with the bot, escalating to tier 1, and finally landing with tier 2 needs context preserved at every hop. Use the conversation property bag, not the message thread:

conversation.intent: support
conversation.product_area: integrations
conversation.tier: 2
conversation.bot_attempts: 3
conversation.assigned_history: ["bot", "rep_42", "rep_57"]

The receiving rep opens the conversation and sees the route, not just a chat log.

Measure routing health, not just chat volume

Build a dashboard with: time-to-first-response by intent, mis-routes per week (manual reassignments count as a miss), abandoned chats during business hours, and load distribution per rep. Routing tuning is a weekly exercise, not a one-time setup.

What to do this week

Tag each rep with skill values, add an intent classifier on the first chat message, replace round-robin with load-aware routing for your highest-value queue, and instrument mis-route counts on the dashboard.

[object Object]
Share