Workload-type capacity in Amazon Connect: what actually changes
Listen to article
Fernando's voiceFernando · 15:54
Powered by Amazon Polly + OmniVoice
Amazon Connect now lets you set concurrency per workload type inside the Task and Email channels instead of per channel. The operational gain is real on high-variance queues — disputes, KYC, regulated back office. The price is a new taxonomy to govern and a failure mode that does not shout: a contact whose workload type has no matching row in the routing profile stays queued forever.
For years, Amazon Connect concurrency treated every contact on a channel as if it cost the same effort. A statement-copy request and a twelve-attachment chargeback dossier landed in the same "up to 5 emails per agent" bucket, and the math only balanced on the average — never on the tail. Workload-type capacity, launched on September 9, 2026 for Task and Email, breaks that bucket. It is a small change on the configuration screen and a large one in staffing arithmetic, and it ships with a failure mode that deserves an audit before any pilot.
The three numbers that shape the design
connect:WorkloadType attribute takes up to 500 values; the routing profile takes 5 rows per channel. The big taxonomy does not fit the configuration.MediaConcurrency documents Channel, Concurrency and CrossChannelBehavior. Nothing else.What actually changed in the routing mechanics
The old model is easy to describe: the routing profile sets Maximum contacts per agent per channel, plus one CrossChannelBehavior per channel. Four channels, four rows, done.
The new model inserts a dimension between the channel and the contact. There is a system predefined attribute, connect:WorkloadType, that you populate with your own values — Dispute-Review, KYC-Refresh, Statement-Request. The contact gets the value from the Set contact attributes flow block or from the UpdateContact API. In the routing profile, the Task or Email channel stops having one row and gets up to five, each with its own concurrency from 1 to 10 and its own cross-channel behavior.
Cross-channel behavior now has three options, and the middle one is the interesting one: no other channels or workload types, only other workload types of the same channel, allow other channels concurrently. You can state that an agent on Dispute-Review receives absolutely nothing else, while an agent on Statement-Request stacks three of the same class and still accepts a chat.
Two hard constraints. First: a channel cannot mix the two models — enabling workload-type concurrency disables channel-level concurrency on that channel, and the old fields go inactive. Second: a contact with no explicit workload type falls back to its subtype, which means a partial migration does not break existing traffic, as long as a row exists for that subtype.
Lifecycle of a contact under workload-type concurrency
The connect:WorkloadType value is decided early, in the flow or through the API, and determines which routing profile row governs the agent slot. The dashed path to 'no matching row' is the one that raises no error.
- E-mail de entrada · domínio da instância
- Task via API · ou a partir de um caso
- Set contact attributes · connect:WorkloadType
- UpdateContact · 10 TPS / burst 15
- Queue · FIFO por prioridade e delay
- Routing profile · até 5 linhas por canal
- Dispute-Review · conc. 1 · nenhum outro
- Statement-Request · conc. 3 · mesmo canal
- Sem linha correspondente · contato fica em fila
- Agente no CCP · slots por workload type
- GetCurrentMetricData · SLOTS_AVAILABLE · SLOTS_ACTIVE
- CloudWatch · ConcurrentEmailsPercentage > 80%
Where this genuinely wins
UpdateContact, so the classifier can be a business rule, a lookup, or a model — without touching the routing profile.The queue this fixes: card disputes
Take a payments operation with two kinds of email in the same queue. Statement-Request is a system lookup and a template reply: four minutes, no attachments, no decision. Dispute-Review is a dossier — receipts, authorization history, a regulatory clock running, and a decision that turns into money refunded or not refunded.
Under channel-level concurrency with Concurrency = 5, the agent gets all five. Four are templates and one is a dossier, and the dossier loses. It does not lose for lack of staffing: it loses because the agent is interrupted by four trivial items while reading an authorization statement. What shows up in the report is average handle time drifting upward with no culprit.
The new configuration says the obvious out loud: Dispute-Review at concurrency 1 with no other channels or workload types; Statement-Request at concurrency 3 with only other workload types of the same channel. That sums to 4, inside the per-channel ceiling of 10.
Two numbers sit next to this. Email costs US$ 0.080 per message sent or received — every thread reopened by a rushed reply is a direct cost on top of the rework. And an email contact expires after 14 days by default, adjustable up to 90 through the connect:ContactExpiry attribute. In a dispute with a regulatory deadline, those two numbers have to line up with the rule's clock before anyone argues about concurrency.
The failure mode that raises no error
If a contact arrives with a connect:WorkloadType value that has no matching row in the agent's routing profile, it stays queued indefinitely. No exception, no dedicated metric, no bounced email — the contact is simply never offered. That turns a drift between flow and routing profile into a silent SLA incident, and the drift is easy to create: someone adds a new value in the Set contact attributes block and forgets one routing profile. Before the pilot, extract every value used in flows and in UpdateContact calls, diff it against DescribeRoutingProfile for each profile, and fail the pipeline on the difference.
The gap between the console and Terraform
Here is my most serious criticism of the launch, and it is not about routing: it is about how the configuration travels between environments.
The public API that touches routing profile concurrency is UpdateRoutingProfileConcurrency, and the object it takes is MediaConcurrency. That object documents exactly three fields: Channel with values VOICE | CHAT | TASK | EMAIL, Concurrency from 1 to 10, and CrossChannelBehavior. There is no workload-type field. Configuring the five rows per channel is described as an admin console flow — and what has no API shape has no Terraform module shape, no plan, no pull request diff.
The practical consequence is familiar to anyone who has operated a contact center for a while: production configuration starts drifting from staging along paths nobody reviews. In an audited operation — BACEN, PCI-DSS, any regime that demands change control — "someone with the right security profile changed it in the console" is an expensive answer to defend.
What I would do meanwhile: keep the workload-type taxonomy as a versioned artifact in the repository, with an owner, a definition and a target concurrency per routing profile; run a daily job that reads DescribeRoutingProfile and ListPredefinedAttributes and compares against the artifact; and treat drift as an incident, not a backlog item. It is manual reconciliation — but it is auditable reconciliation, which is what the audit actually asks for.
The two models, side by side
| Channel-level concurrency | Workload-type concurrency | |
|---|---|---|
| Granularity | One row per channel, 4 rows total. | Up to 5 rows per channel on Task and Email, sum ≤ 10. |
| Configuration path | Console and UpdateRoutingProfileConcurrency through MediaConcurrency. | Admin console; the API shape is not documented in MediaConcurrency. |
| Configuration failure | Channel disabled: visible behavior, nobody receives. | Value with no row: the contact sits in queue with no error and no alert. |
Observability: what to measure after you turn it on
The promise is capacity that matches reality. Three signals prove it, and it is worth checking the dimensionality of each one on your instance before promising anyone a report.
Slots. SLOTS_AVAILABLE — surfaced as Availability — counts how many free slots each agent has for new contacts, respecting the routing profile limits and ignoring agents in a custom status. SLOTS_ACTIVE counts the ones occupied by contacts that are connected, on hold, in ACW or paused. Both come from GetCurrentMetricData, capped at 5 TPS with a burst of 8 — this is not an endpoint for one-second polling.
Real concurrency. AVG_AGENT_CONCURRENCY, in GetMetricDataV2, is concurrent handle time divided by handle plus idle time. A value near 1 means the agent is working sequentially. If you configured concurrency 3 on a workload type and the metric sits at 1.1, the configuration is not being exercised — either there is no volume, or the cross-channel rule is blocking upstream.
Instance saturation. ConcurrentEmails and ConcurrentEmailsPercentage in CloudWatch guard the ceiling of 1,000 active emails per instance; for Task the ceiling is 2,500 concurrent. The documentation's own recommendation is an alarm at 80% of quota. Breaching it does not degrade gracefully: the API call fails with a quota-exceeded error.
The signal that does not exist out of the box is the one for the failure mode above. Oldest contact age per workload type, alarmed at a multiple of expected wait, is the proxy that closes that gap.
How to adopt this without creating a pager
- 1
Measure the distribution before choosing the cuts
Export handle time per Task and Email queue for 30 days and look at p50, p90 and p99. If p90 sits within 2× of p50, the queue lacks the variance to justify workload types — channel-level concurrency remains the right answer.
- 2
Write the taxonomy as an artifact, not as console typing
Five rows per channel against 500 possible
connect:WorkloadTypevalues means the taxonomy needs an owner and a merge criterion from day one. Version the name, definition, target concurrency and cross-channel behavior in the repository. - 3
Keep PII out of the type name
The documentation is explicit: predefined attribute information is not encrypted.
VIP-Callbackis fine; any value carrying a name, tax ID or account number becomes personal data exposed in telemetry and reporting — LGPD included. - 4
Close the flow ↔ routing profile loop in the pipeline
A job that reads the values emitted by flows and by
UpdateContactand compares them withDescribeRoutingProfilefor each profile, failing on the difference. It is the only defense against the contact that queues forever. - 5
Enable on one routing profile and plan the coexistence
When switching models, existing contacts drain under the old rules while new ones follow the new configuration. Pick a low-volume window and measure nothing during the coexistence period — the number means nothing there.
Anti-patterns you can already predict
- One row per product: modeling workload type by product line burns the 5 rows before expressing any effort difference. The useful dimension is cost of attention, not commercial taxonomy.
- Summing to 10 because the ceiling is 10: the channel ceiling is a maximum budget, not a target. Summing to 10 on Email across four types guarantees the most expensive type gets interrupted by the other three.
- Classifying with a model and no fallback value: if the classifier emits a new label in production and no row exists for it, the contact disappears from the operation with no signal. Pin the output set and map unknown to the subtype.
- Reconfiguring in production during peak: switching models creates a coexistence window, and coexistence with a full queue is the worst moment to interpret any capacity metric.
I would enable this on exactly one queue — the one with the widest handle-time variance — with two workload types, not five. Two types fit in an operator's head and produce a clean signal in AVG_AGENT_CONCURRENCY within two weeks; five produce a spreadsheet nobody reviews. Before touching the routing profile, I would write the reconciliation job between the values emitted in flows and the rows in the profile, because the lesson that has already cost me a night is this: the dangerous defect is not the one that takes the system down — it is the one that makes a customer contact vanish without producing a single log line. And while MediaConcurrency has no workload-type field, I would treat every change to this configuration as a manual production change, with a record and an owner, not as a screen tweak.
References
Verdict
Adopt it on Task and Email when the queue has real effort variance — p90 above 2× p50 — and when someone owns the workload-type taxonomy. In that case it corrects a modeling error that has been in the platform since Task existed, and the gain shows up in the tail, exactly where the SLA breaks. Hold off in three situations: an operation under strict change control, while MediaConcurrency exposes no workload type and the configuration lives only in the console; a homogeneous queue, where five rows only add error surface; and any adoption that starts before the job exists that compares the values emitted in flows against the routing profile rows — without it, the price of the feature is a customer contact stuck in queue with no alarm.
Architecture, AWS, AI and market deep dives — straight to your inbox. Free.
No spam · unsubscribe anytime
Ask Fernando about this
Get a focused answer about this article from my AI assistant, grounded in my work.
Join the conversation
Sign in to comment
Verify your email to join in — you'll also get the newsletter. No password.
Keep reading
Architecture intelligence, in your inbox
Curated signals and original analysis on AWS, AI, distributed systems and the market — the way a solutions architect reads them.
- Curated AWS · AI · architecture · market signals
- New architecture studies & deep-dives when they ship
- Sharp summaries — depth without the noise
- No spam · double opt-in · unsubscribe anytime