A warning light on your car's dashboard tells you something is wrong. But it takes a service request, a mechanic, and a bit of follow-through to actually fix it.
Power BI isn't that different. It can tell us when a customer is falling behind target or a KPI needs attention. But who picks it up? Who investigates? And how do we know anything happened afterwards?
That's the idea behind the Action Center we're going to build. We'll connect Power BI, Power Automate, and Power Apps to turn detected problems into actions people can own, investigate, and follow through on. Because spotting a problem is only useful if something happens next.
In this blog
- Why not every red KPI deserves an action?
- Building an Action Center around Power BI
- Connecting investigation, ownership, and context
- Keeping exceptions from becoming endless tasks
Not Every Red Number Deserves an Action
The easiest way to build an Action Center would be to take every KPI that crosses a threshold and turn it into a task. It would also be a pretty good way to make people stop using it.
A number falling below target doesn't necessarily mean someone needs to intervene. Imagine a customer whose performance drops 12% below target for a single day, then returns to normal. Creating a task every time that happens would quickly fill our Action Center with noise. But if the same customer remains below target for several reporting cycles, that might be worth investigating.
For this implementation, I'd qualify an exception using three conditions: the deviation is meaningful, someone can take responsibility for it, and there's a clear next action. The threshold tells us where to look, but the business context determines whether something deserves attention.
This distinction matters for the architecture we're about to build. Power BI identifies exceptions based on agreed business rules. The Action Center manages what happens after they're detected. It shouldn't automatically assume that every deviation requires a new task, or that a KPI returning to normal means the investigation is complete.
Making Power BI Tell Us What Needs Attention
Here's where the semantic model becomes useful beyond powering reports. If we've already defined our KPIs and business rules in Power BI, why rebuild the same logic somewhere else? We can query the model directly and use the results to identify what needs attention.
Let's take a simple example: customers whose performance falls more than 10% below target. Using the Run a query against a dataset action in Power Automate, we can execute a DAX query against our published semantic model.
-- Returns customers performing more than 10% below target.
-- Excludes blank actuals and non-positive targets.
-- Outputs the customer ID, actual performance, and target.
EVALUATE
VAR _Customers =
ADDCOLUMNS(
VALUES('Customer'[CustomerID]),
"Actual", [Actual Performance],
"Target", [Performance Target]
)
RETURN
SELECTCOLUMNS(
FILTER(
_Customers,
NOT ISBLANK([Actual]) &&
[Target] > 0 &&
[Actual] < [Target] * 0.9
),
"EntityID", 'Customer'[CustomerID],
"Actual", [Actual],
"Target", [Target]
)
The query returns only customers meeting our condition. But we don't want Power Automate to treat every returned row as a brand-new task. We need a way to recognise the same exception across multiple runs.
From Query Results to Actionable Records
I'd start with a small Dataverse table called Exceptions. Alongside the customer identifier and KPI values, it needs a few fields that allow us to manage the exception over time.
| FIELD | PURPOSE |
|---|---|
ExceptionKey |
Uniquely identifies the customer and exception type |
EntityID / ExceptionType |
Identifies what triggered the exception |
Actual / Target |
Preserves the latest detected values |
FirstDetected / LastDetected |
Tracks when the issue first appeared and was last observed |
Status / AssignedTo |
Captures the human side of the workflow |
For our example, the exception key could be CUST001|BelowTarget. The customer identifier comes from the DAX query, while BelowTarget is a fixed exception type configured in the flow.
I'd schedule Power Automate to run after a successful semantic model refresh, execute the DAX query, and process each returned row. Using the exception key, the flow can find the corresponding Dataverse record and either create it or update its latest KPI values. We'll handle the full lifecycle, including what happens when an exception disappears, later in the article.
The important design choice is that the automation owns the detected values, while people own the investigation. A refresh can update Actual, Target, and LastDetected, but it shouldn't overwrite someone's assigned owner, comments, or resolution status. There are also a few practical things I'd watch out for:
Run exception detection only after a successful semantic model refresh.
Ensure the flow's Power BI connection can access all relevant data.
Retry safely without creating duplicate exception records.
For thousands of exceptions or near-real-time detection, I'd consider moving this logic closer to the data platform rather than relying on scheduled semantic model queries. We now have something more useful than a daily export of flagged customers: a persistent set of exceptions that can survive reporting cycles. Next, we need to give people a practical way to work with them.
From a List of Problems to a Place to Act
We now have our exceptions in Dataverse, with the latest KPI values and enough information to recognise them across refreshes. But a table full of exceptions isn't an Action Center yet. The missing piece is a way for someone to pick up an issue, understand what needs attention, and record what they're doing about it.
This is where I'd use Power Apps. We'll build a small canvas app connected to the Exceptions table we created earlier. Rather than designing an elaborate application, I'd focus on two screens that support the actual workflow.
Building the Action Center
The first screen is an exception queue. It should immediately answer three questions: What needs attention? How urgent is it? And is anyone already handling it?
I'd show the exception type, customer, latest deviation, owner, and status, with filters for unassigned and in-progress items. Selecting a record opens the second screen, where someone can review the detected values, take ownership, and record their investigation.
The key is that both screens work with the same Dataverse record. We aren't copying exceptions into another list or creating a separate task whenever someone clicks a button.
For example, when someone selects Take Ownership, we can update the record directly:
Patch(
Exceptions,
galExceptions.Selected,
{
AssignedToEmail: User().Email,
Status: "In Progress"
}
)
Here, AssignedToEmail and Status are simple text columns in our prototype. For a production application, I'd use an appropriate user lookup and a Dataverse Choice column instead. The button is straightforward. What's more important is what happens afterwards.
Suppose our customer remains below target for another three days. Power Automate will continue updating the detected performance values, but the assigned owner and investigation status should remain untouched. Someone might already be reviewing the issue, even while the numbers continue to change.
โThat's the boundary I'd protect: automation updates what we know about the problem; people update what they're doing about it.
โ
Give the Investigation Somewhere to Live
Taking ownership is only the beginning. On the detail screen, I'd include a short investigation note, an optional next-action date, and a way to mark the work as resolved. These fields belong in Dataverse alongside the exception, so the next person doesn't have to reconstruct the discussion from emails or chat messages.
I'd also capture who last updated the investigation and when. If several people work with the same queue, that context becomes useful surprisingly quickly.
| CUST001 Customer performance investigation | In progress |
Performance decline detected. Checking recent service activity to determine whether further action is needed.
| NEXT ACTION Validate findings with account team | NEXT REVIEW 20 Oct 2026 |
One practical consideration is permissions. Sharing the Power App doesn't automatically grant access to its Dataverse data. We need to configure the relevant security roles and decide whether users can update every exception or only those belonging to their team.
At this point, we have a working queue, ownership, and a place to record progress. But we shouldn't try to reproduce the entire dashboard inside Power Apps. The Action Center should tell someone what needs investigating, while Power BI should still help them understand why.
The Dashboard Is Still There. Just Not First.
Our Action Center now gives someone a place to pick up an exception, take ownership, and record their progress. But there's an obvious limitation: knowing that a customer's performance is 15% below target doesn't explain why it happened. And that's where Power BI comes back into the picture.
Rather than recreating charts and analysis inside Power Apps, I'd add an Investigate in Power BI button that takes the user directly to the relevant report page, with the customer already selected. The idea is to remove the unnecessary steps between spotting an exception and investigating it.
Connecting the Investigation
Power BI supports URL filters, so we can pass the customer identifier from our selected Dataverse record into the report. In Power Apps, the button could look like this:
Launch(
varReportURL &
"&filter=" &
EncodeUrl(
"Customer/CustomerID eq '" &
varSelectedException.EntityID &
"'"
)
)
Here, varReportURL contains the URL of our dedicated investigation page, including its existing query parameters, and varSelectedException.EntityID comes from the exception selected in Power Apps.
For this to work reliably, I'd pay attention to a few details. The table and column in the URL filter must match the Power BI model, users still need permission to access the report, and URL filters won't automatically recreate every slicer selection. If the base URL has no query parameters, use ?filter= instead of &filter=.
?filter= or &filter=
I'd also avoid hardcoding report links into individual buttons. As the solution grows, a small configuration table mapping exception types to investigation pages would make it easier to maintain. A performance exception could open one report page, while a compliance exception could open another.
Don't Lose the Context Along the Way
A deep link is useful, but opening the right report isn't enough on its own. Imagine someone picks up an exception on Monday, but only investigates it on Thursday. By then, the semantic model might have refreshed several times, and the customer could be showing a different performance figure.
That's why I'd keep the original detected values and detection date available in the Action Center, alongside the latest values. Power BI can show the current analytical picture, while Dataverse preserves what originally triggered the investigation.
There's also a useful distinction here: the Action Center doesn't need to remember every filter someone applied during their analysis. It needs to preserve the exception, its ownership, and the outcome of the investigation. The experience we're aiming for is straightforward:
โ
โ
โ
And that's an important shift. We're no longer expecting someone to browse dashboards hoping to notice something important. We're giving them a specific reason to open Power BI, with a starting point for their investigation. Next, we need to make sure this experience continues working when tomorrow's refresh identifies the same problems all over again.
The Same Problem Shouldn't Become a New Task Tomorrow
We've connected detection, ownership, and investigation. But imagine our customer is still 15% below target tomorrow. Power Automate detects the same exception again. Does that mean someone should receive another task?
Of course not. And this is where the ExceptionKey we introduced earlier becomes important.
Instead of creating a new Dataverse record for every query result, Power Automate uses that key to recognise whether the exception already exists. I'd configure it as a Dataverse alternate key, so the database also enforces uniqueness rather than relying entirely on the flow to prevent duplicates. The flow can then handle four situations:
ExceptionKey
|
01 / NEW EXCEPTION
Create
New record + team notification
|
02 / STILL DETECTED
Update
Refresh KPI values and LastDetected; retain ownership and progress
|
|
03 / NO LONGER DETECTED
Mark absent
Update detection state; keep the investigation open
|
04 / RETURNS AFTER RESOLUTION
Reopen / New
Reopen the record or create a new occurrence under agreed rules
|
There's an important distinction here: whether the problem still exists in the data and whether the work has been resolved are two different things.
I'd represent them separately. For example, DetectionState could tell us whether Power BI still detects the exception, while Status tracks whether someone has acknowledged, investigated, or resolved it. That prevents a refreshed KPI from silently closing work that still needs attention.
Don't Let a Failed Refresh Close Everything
There's one scenario I'd be particularly careful with. Suppose our query normally returns 50 exceptions, but tomorrow it returns none. It would be tempting to mark all 50 as cleared. But what if the semantic model hasn't refreshed, the query returned incomplete results, or the flow failed halfway through?
Before changing the detection state of exceptions that weren't returned, I'd verify that the refresh and query completed successfully and that the results represent the full intended scope. Only then should the flow compare the current results against previously detected exceptions.
For larger batches, I'd also track a processing run identifier. That makes it easier to distinguish a completed detection cycle from one that stopped halfway through.
And I'd keep notifications selective. A new exception deserves attention, but an unchanged one doesn't need another daily email. Reminders can be introduced for overdue or worsening issues, rather than simply because the same condition appeared again.
This is what makes the Action Center dependable across reporting cycles. The data can change every day without resetting the work people have already started.
Did We Build Something Worth Using?
At this point, we have a working Action Center. Power BI identifies exceptions, Power Automate keeps the records updated, and Power Apps gives people somewhere to investigate and manage them.
But before rolling this out more widely, I'd want to know whether it's actually improving how the team works. I'd start with a small pilot, using one exception type and a handful of users. Not to see whether the application works technically, but to understand what changes when people start using it.
I'd pay particular attention to the exceptions that nobody picks up. Are they being sent to the wrong team? Is the threshold too sensitive? Or is the issue simply not important enough to justify an action? Those are useful findings, because they tell us what needs to change in the detection rules or the workflow.
Does It Really Need Its Own Application?
There's another decision worth making before scaling the solution: whether Power Apps and Dataverse are the right level of investment.
|
|
|
||||||||||||
| Already have a ticketing system? Route exceptions into the existing workflow instead of building another. | REUSE FIRST |
I'd make the decision based on what the pilot reveals, not how sophisticated the solution looks. If people only need a weekly list of five issues, an Action Center might be unnecessary. If exceptions regularly move between teams, remain unresolved across reporting cycles, or require a reliable investigation history, the additional structure starts to earn its place.
The question isn't how many exceptions we detected. It's how many important issues actually moved forward because someone had a clearer way to act.
Closing Thoughts
I like the idea of an Action Center, but I wouldn't rush to build one just because the technology makes it possible. In plenty of organisations, people already have too many applications to check, and adding another one might create more work than it removes.
Where I genuinely see value is when problems keep getting noticed but nobody has a clear view of what's happening afterwards. Even then, I'd first look at whether the workflow could fit into something people already use. Sometimes a well-designed notification or a simple shared list might do more good than an entire application.
And I think that's something we should challenge ourselves on more often in BI. We spend so much time thinking about what else we can build that we don't always question whether we're making someone's job simpler. Because the goal was never to build another place to work. It was to make sure the work actually gets done.
Comments
Join the discussion below (GitHub login required), or share your thoughts on LinkedIn . Iโm most active there.