# Workload-type capacity in Amazon Connect: what actually changes

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.

- URL: https://fernando.moretes.com/blog/capacidade-por-tipo-de-trabalho-no-amazon-connect-o-que-muda-de-fato-amazon-conne

- Markdown: https://fernando.moretes.com/blog/capacidade-por-tipo-de-trabalho-no-amazon-connect-o-que-muda-de-fato-amazon-conne/article.md?lang=en

- Published: 2026-09-10T10:15:49.899Z

- Category: AI & Agents

- Tags: amazon-connect, routing, capacity-planning, contact-center, observability, iac, aws

- Reading time: 8 min

- Source: [Amazon Connect Customer now lets you set specific capacity limits for different types of Tasks and Emails](https://aws.amazon.com/about-aws/whats-new/2026/09/amazon-connect-capacity-limits/)

---

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

- **5** — workload types per channel, per routing profile. The `connect:WorkloadType` attribute takes up to 500 values; the routing profile takes 5 rows per channel. The big taxonomy does not fit the configuration.
- **1–10** — concurrency per row, sum ≤ 10 per channel. Each workload type gets its own ceiling, but the channel's total budget is still 10 slots.
- **0** — workload-type fields in the concurrency API. `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.

### 📥 Origem — trabalho entrando

- E-mail de entrada domínio da instância (external)
- Task via API ou a partir de um caso (external)

### 🔀 Classificação — onde o tipo é decidido

- Set contact attributes connect:WorkloadType (compute)
- UpdateContact 10 TPS / burst 15 (compute)

### 🟧 AWS — roteamento do Amazon Connect

- Queue FIFO por prioridade e delay (messaging)
- Routing profile até 5 linhas por canal (compute)
- Dispute-Review conc. 1 · nenhum outro (data)
- Statement-Request conc. 3 · mesmo canal (data)
- Sem linha correspondente contato fica em fila (security)

### 👤 Agente — onde o slot é consumido

- Agente no CCP slots por workload type (user)

### 📡 Observabilidade — o que prova que funcionou

- GetCurrentMetricData SLOTS_AVAILABLE · SLOTS_ACTIVE (network)
- CloudWatch ConcurrentEmailsPercentage > 80% (security)

### Flows

- email -> flow: enters the inbound flow
- task -> api: classified outside the flow
- flow -> queue: workload type set
- api -> queue: attribute applied to contact
- queue -> rp: match agent with a free slot
- rp -> wtA: high attention
- rp -> wtB: low effort
- rp -> limbo: value with no row: no error
- wtA -> agent: 1 slot, blocks everything else
- wtB -> agent: up to 3 concurrent
- agent -> metrics: free and active slots
- metrics -> alarm: channel saturation
- limbo -> alarm: oldest contact age

## Where this genuinely wins

- **Staffing by distribution, not by average:** email queues with handle times between 2 and 90 minutes never balanced on a single number. Now they balance on up to five.
- **Interruption stops being all-or-nothing:** allowing only other workload types of the same channel creates a middle tier that the channel model never had.
- **Classification where the information lives:** the value comes from the flow block or from `UpdateContact`, so the classifier can be a business rule, a lookup, or a model — without touching the routing profile.
- **Incremental migration:** contacts with no explicit workload type fall back to subtype, so you can enable this channel by channel and queue by queue.
- **Broad availability at launch:** every commercial Region and GovCloud (US-West) where Amazon Connect is offered — not a two-Region preview.

## 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
| Criterion | 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:WorkloadType` values 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-Callback` is 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 `UpdateContact` and compares them with `DescribeRoutingProfile` for 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.

> **What I would actually do:** 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

- [AWS What's New — Amazon Connect: specific capacity limits for Tasks and Emails (Sep 9, 2026)](https://aws.amazon.com/about-aws/whats-new/2026/09/amazon-connect-capacity-limits/)
- [Amazon Connect Administrator Guide — Channels and concurrency (workload-type concurrency)](https://docs.aws.amazon.com/connect/latest/adminguide/channels-and-concurrency.html)
- [Amazon Connect Administrator Guide — Create a routing profile](https://docs.aws.amazon.com/connect/latest/adminguide/routing-profiles.html)
- [Amazon Connect Administrator Guide — Predefined attributes (connect:WorkloadType)](https://docs.aws.amazon.com/connect/latest/adminguide/predefined-attributes.html)
- [Amazon Connect service quotas — concurrent tasks, emails and API throttling](https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-service-limits.html)
- [Amazon Connect API Reference — MediaConcurrency](https://docs.aws.amazon.com/connect/latest/APIReference/API_MediaConcurrency.html)
- [Amazon Connect Administrator Guide — Real-time metrics definitions (SLOTS_AVAILABLE, AVG_AGENT_CONCURRENCY)](https://docs.aws.amazon.com/connect/latest/adminguide/real-time-metrics-definitions.html)
- [Amazon Connect pricing — Email and Chat](https://aws.amazon.com/connect/pricing/)

## 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.

**Rating:** 8/10
