The problem
Support was three inboxes and a WhatsApp on someone's personal phone. Customers repeated themselves, agents collided on the same ticket, and nobody could answer "how long do we take to reply?"
What I built
- One thread per customer, merging web chat, WhatsApp and email by contact identity.
- Presence and typing indicators over WebSockets, with an assignment lock so two agents cannot answer at once.
- Canned replies with variables, searchable by keystroke.
- Full-text search across every message, with filters for channel, agent and date.
- A metrics strip: first response time, resolution time, open threads per agent.
Decisions worth explaining
Optimistic sends with a visible failure state. A message appears instantly and shows a clear retry if the socket dropped. Agents trust the interface because it never silently loses a message.
Virtualised everything. Threads with four thousand messages scroll at sixty frames per second because only what is visible is mounted.
Results
- Median first response 14 minutes → 4.
- Duplicate replies effectively eliminated by the assignment lock.
- Agents handle around 40% more threads per shift.