Messaging concurrency and capacity: how hot is your team running?
Most reporting tools will tell you how many conversations your team handled. Very few will tell you how many each agent was holding at the same time — and almost none will tell you how long they have been holding them. That second question is the one that predicts a bad afternoon: response times stretch, quality slips, and the first visible symptom is usually a customer complaint rather than a number on a dashboard.
Qvasa reports messaging concurrency at three levels, from the same underlying data, so the numbers always reconcile:
Live — what the bench is carrying this second, including who is over the line and for how long.
By agent — who carries more than their share, live and averaged over any period.
Historical — the shape of your day and your week, so staffing decisions are based on the pattern rather than on yesterday.
This article explains what Qvasa counts, walks through each of those three views, and ends with a dashboard you can build in about ten minutes.
First, what Qvasa counts: the messaging work item
Every concurrency number in this article is built from one thing: the messaging work item. It is worth understanding, because it is the difference between a number you can act on and a number you have to argue about.
A work item is Zendesk's own record of a piece of work being handed to a specific agent on a specific channel. When omnichannel routing offers a conversation to an agent, and when that agent accepts it and the session becomes active, Zendesk emits an event. Qvasa receives those events as they happen and stores each one as a work item with the agent it belongs to, the channel it arrived on, the ticket behind it, and the exact moments it opened and closed.
Two states count as occupied on Messaging:
Offered — the conversation has been routed to the agent and is waiting for them to take it. An offer holds a slot from the moment it is made until it is accepted or missed, which is deliberate: a slot that is spoken for is not free capacity, even though nobody is typing in it yet.
Active session — the agent is in the conversation.
So an agent's concurrency at any instant is simply the number of their messaging work items that are open at that instant. No sampling, no polling, no estimate from message counts. Because each work item carries its own start and end, Qvasa can answer the question at any moment in the past just as precisely as it can answer it for right now — which is what makes the historical views in this article possible at all.
Two details that matter when you read the numbers:
An agent holding three chats is one occupied agent, not three. "Occupied agents" counts people; concurrency counts conversations per person. Both are on the board below, and they are supposed to differ.
A work item that nothing ever closed is ignored once it is more than seven days old. Feeds occasionally drop a closing event, and without this rule one stuck record would quietly inflate every number for weeks.
Before you start
Two things need to be switched on for your account, and your Qvasa contact can confirm both in a minute:
Agent status tracking — Qvasa needs to know who is online on Messaging, because that is the denominator of every ratio here.
Messaging work item events — the per-agent and distribution metrics need the work items themselves. Without this you will still get the account-wide ratios, but not the per-agent breakdown or the distribution.
Everything below is built with ordinary dashboard widgets, so anyone who can edit a dashboard can build it. No special permissions are required.
Right now: the live board
This is the view a real-time analyst or a team lead keeps open. Every widget on it is live — it reads the current moment and refreshes itself, and the date range on the dashboard does not apply to it.
Reading the top row left to right:
Messaging Concurrency — 2.20. Open chats divided by online messaging agents, as a plain count of conversations. This is the number to put in front of people: "our agents are averaging 2.2 chats each right now" needs no explanation.
Messaging Concurrency % — 220.0%. Exactly the same reading expressed as a percentage. It is the older form of the metric and it is uncapped, so anything above 100% means more than one chat per agent. Both widgets exist so you can keep a percentage you have historically reported while moving your team to the count.
Messaging Capacity Utilization % — 73.33%. This one knows your limit. Instead of dividing by agents, it divides by the slots those agents have: set the concurrency limit you actually run (this board is set to 3) and the widget tells you how full the bench is. 100% means every slot is spoken for and the next conversation waits. You can also tick a box to use each agent's own configured concurrency instead of a flat number, which is the right choice when different teams run different limits.
Messaging Occupied Agents — 9. How many people are holding at least one chat. Compare it to your online count to see how much of the bench is engaged at all.
The second row is where the real-time value sits, and it is the part most tools cannot answer.
Agents at 2+ Chats — 8 — and Agents at 3+ Chats — 4. Pick any level from 0 to 10 and choose whether you mean exactly that many or that many or more. Two copies of the same widget at different levels give you the shape of the bench at a glance: eight people are doubled up, and half of those are past the limit this team runs. Click either number and the drill-in names those agents, so "who is at three" takes one click rather than a Slack thread.
Agents at 2+ Chats for 10m — 7. The same question with endurance attached: how many have been at that level continuously for at least ten minutes. Anyone can spike to three chats for ninety seconds; sitting there for a quarter of an hour is what degrades replies. Set the level and the minutes (anything from 1 to 1440) to match how your team actually works.
Longest Active 2+ Chat Streak — 1h 1m. The single longest run anyone is currently having at that level, as a duration, with a drill-in that names the one person. When this creeps past what you consider reasonable, you have a staffing problem happening right now rather than one you will read about tomorrow.
One honest note on the last two. They are measured from the chats that are still open, so a streak starts at the moment the agent's Nth-oldest open chat began. That makes them a lower bound: an agent who dropped from three chats to one and climbed back to three reads from the newer run. For a live pressure gauge that is the behaviour you want. Just do not read it as a complete history of the shift — the historical views below are for that.
Who is carrying it: concurrency by agent
An account-wide average hides the thing you most need to see, which is that the load is rarely shared evenly.
Live Messaging Occupancy by Agent — how many conversations each person is holding this second. This is your "who is maxed out and who can take more" view, and it is the one to look at before you route anything by hand.
Avg Messaging Concurrency by Agent — over the date range you choose, how many conversations each agent held at a time while they were online. Being online but idle pulls an agent's average down; being offline does not, because the measure only runs while they are clocked in on the channel. Ranked over a week, this separates the people genuinely carrying more load from the people who simply worked more hours.
Both are available as a chart or as a column on the agents table, where they sit beside your other per-agent measures. The historical one also works over time, so you can chart a single agent's concurrency day by day.
Over time: the shape of the day
Live numbers tell you about now. To staff properly you need the pattern.
This chart breaks every bucket into the share of online agent time spent at each level — 0 chats, 1, 2, 3, 4, 5 or more — and it always sums to 100% of the time your agents were online. It answers a question a single average cannot: on your busiest day, was everyone at two, or was half the team idle while four people sat at four? Those two situations produce the same average and call for completely different responses.
Alongside it, Agents at Concurrency over time charts the count and the rate at a level you pick, so you can watch "how many people are past our limit" hour by hour across a week.
The heat map: concurrency by day and hour
This is the single most useful artefact in the article, and it takes about thirty seconds to build once you know it exists.
Qvasa's heat map widget wraps any over-time chart and re-grids it as day of week by hour. Point it at the concurrency count and you get your whole week on one screen, with the actual decimal in every cell rather than a colour you have to interpret.
The example above is a fortnight of one team's data, and it reads at a glance: nothing before 2 PM, a steady climb through the afternoon, a hard peak of 2.1 on Wednesday and 2.0 on Thursday early evening, tapering by 10 PM, and weekends closed. If your target is two chats an agent, this picture tells you precisely which four hours of which two days need another person — and that adding cover on Monday morning would be spending money on an empty room.
The caption under the grid always names how the cell was produced. Concurrency is a ratio rather than a simple count, so the heat map assembles it from hourly figures and says so ("hourly approximation"); a cell is the average across the days that had data. Counts and summed durations are grouped exactly, and for those you also get a Cell value choice of Total or Average per day, plus a Divide by choice between every matching day in the range and only the days that had data.
Build the dashboard yourself
Roughly ten minutes end to end:
Create a dashboard and add two tabs, Live now and Over time. Tabs keep live widgets away from the date range, which only applies to the historical ones.
On Live now, add a row and search the widget library for concurrency. Add Messaging Concurrency Number, Messaging Concurrency Per Online Agent, Messaging Capacity Utilization and Messaging Occupied Agents. Set the concurrency limit on the capacity widget to the number your team actually runs.
Add a second row with Messaging Agents At Concurrency Count twice — set one to level 2 and one to level 3, both "at or above" — then Messaging Agents Held At Concurrency Count (level 2, 10 minutes) and Messaging Longest Active Concurrency Streak (level 2).
Add a third row with Live Messaging Occupancy Count By Agent and Messaging Average Concurrency By Agent.
On Over time, set the date range to a couple of weeks and add Messaging Concurrency Number Over Time, then Messaging Agents By Concurrency Level Percentages Over Time.
Add a heat map, and when it asks which chart to grid, choose Messaging Concurrency Number Over Time. Pick day of week by hour.
Tip: pick your levels to match your own limit rather than copying ours. If your team runs a limit of two, "3+" is your overflow indicator; if it runs four, watch 4+ and 5+ instead.
Troubleshooting
The per-agent and distribution widgets are missing from the library. Messaging work item events are not switched on for your account. The account-wide ratios will still be there; ask your Qvasa contact to enable the work item feed.
Concurrency looks lower than you expect. Check how many agents are online but idle. Anyone clocked in on Messaging is in the denominator whether or not they are handling anything, so a large always-online group flattens the ratio. Filter the widget to the team you mean.
A live widget shows nothing where you expected a zero. The streak widget is deliberately empty rather than 0 when nobody is at the level, so an empty board is unmistakable.
Live and historical disagree slightly. They look back over different windows by design — the live widgets reach back far enough to catch a chat opened hours ago, while an over-time chart is bounded by your date range. Over any settled period they agree.
If a number still looks wrong, use the drill-in: every count here lists the exact agents behind it, which usually makes the cause obvious in seconds.




