Every Dataverse record has a lifecycle — but without configuration, users can jump between stages in ways that break your business rules. Learn how to define meaningful Status Reason values, enable transition enforcement, and build a governed record lifecycle that the system itself polices. Practical, from first principles, with a full hands-on exercise.

Imagine you're building a grant application management system. A grant application goes through a very deliberate journey: it's drafted, submitted, reviewed, approved or rejected, and eventually closed. The problem your users will run into immediately is this — without any guardrails, someone can accidentally flip an "Approved" application back to "Draft," or mark a "Rejected" one as "Active" again with a single accidental click. The record lifecycle becomes meaningless noise.
This is exactly the problem that Dataverse's Status and Status Reason columns were designed to solve. Every Dataverse table ships with these two special system columns that work together to represent where a record is in its lifecycle. But the real power — the part that most makers miss — is that you can define which status reasons are valid transitions from any given status reason. This means the system itself enforces your business rules about how records are allowed to move through their stages, without you needing to write a single line of code.
By the end of this lesson, you'll be able to configure Status Reason options for any custom Dataverse table, define valid transition paths between those options, and understand how these settings surface in model-driven apps to control what users can do at each stage of a record's life.
What you'll learn:
This lesson assumes you have a working Power Apps environment with permission to create and modify Dataverse tables. You should understand what tables and columns are in Dataverse — if you're new to that, start with Dataverse Fundamentals: Tables, Columns, and Rows Explained for Power Apps Makers before continuing. You should also be comfortable navigating to a table in the Power Apps maker portal (make.powerapps.com).
Before you touch any configuration, you need to understand a distinction that trips up nearly every new maker: Status and Status Reason are two separate columns, and they have a strict parent-child relationship.
Status is the high-level, binary-ish state of a record. For most standard tables, it has exactly two values: Active and Inactive. This column controls whether a record is "alive" or "archived" in a functional sense. Active records appear in default views, participate in lookups, and can generally be edited. Inactive records are essentially read-only by default and hidden from most views.
Status Reason is the child column. Every Status Reason value belongs to exactly one parent Status value. So you might have Status Reason values of "New," "In Review," and "Approved" all belonging to the Active status, while "Cancelled" and "Completed" belong to the Inactive status.
Think of it this way: Status is the chapter, Status Reason is the page. You can only be on a page that exists inside the current chapter.
This matters enormously in practice. If you want a record to be editable in a particular stage, that stage's Status Reason must be parented to Active. If you want a record to become read-only when it reaches a terminal state like "Completed," its Status Reason should be parented to Inactive.
Key insight
You cannot change the values of the Status column itself on custom tables — it will always be Active and Inactive. What you can customize is the set of Status Reason values, which parent Status they belong to, and which transitions between Status Reasons are permitted.
Navigate to make.powerapps.com, select your environment, and open the Tables section in the left navigation. Open any custom table you've created, then click the Columns tab. Scroll down or search for the Status Reason column and open it.
You'll see that by default, custom tables come with two Status Reason values already configured:
These defaults are intentionally minimal. For a real application, you'll replace or supplement these with meaningful business-stage labels. You generally shouldn't delete the default values right away — read on to understand why.
Warning
On standard tables like Account, Contact, or Opportunity, Status Reason values are pre-configured by Microsoft and have specific meanings in the system. Be careful about removing or renaming those. For custom tables, though, you have full flexibility.
Let's walk through building the grant application example from the introduction. You have a custom table called Grant Application. The lifecycle you want to model looks like this:
Active status:
Inactive status:
To add these values, open the Status Reason column on your Grant Application table. In the column editor panel, you'll see the list of existing choices, each with a dropdown indicating which parent Status they belong to.
Click Add new item for each value you want to create. For each item, you'll set:
Work through the full list above. After saving, your Status Reason column will have seven values across both parent states.
Tip
Keep your Status Reason labels short and verb-free — use nouns or adjectives like "Approved" rather than "Has Been Approved." These labels appear in dropdowns, views, and charts, and concise labels read much more cleanly.
Now for the most powerful and least-understood feature: Status Reason Transitions.
Without transitions configured, users can move a record from any Status Reason to any other Status Reason in the same parent Status — and also activate/deactivate records freely. That's the chaos scenario from the introduction. With transitions enabled, you define a specific set of valid "next steps" for each Status Reason value, and Dataverse enforces those paths.
Think of it as a directed graph, or a flowchart. From "Draft," the only valid next step is "Submitted." From "Submitted," you can go to "Under Review" or "Withdrawn." From "Under Review," you can go to "Approved" or "Rejected." Approved and Rejected are terminal Active states that transition to Inactive (Completed and Rejected, respectively).
Here's what that directed graph looks like in text form:
Draft → Submitted
Submitted → Under Review
Submitted → Withdrawn (Inactive)
Under Review → Approved
Under Review → Rejected (Inactive)
Approved → Completed (Inactive)
No skipping steps. No going backward. The system enforces this.
To enable Status Reason Transitions, you work at the table level, not the column level. Here's how:
Once enabled, go back to the Status Reason column for the table and open it. You'll now see a new control next to each Status Reason value — a Transitions button or link. Clicking it opens a configuration panel where you can select which Status Reason values are valid destinations from this particular source.
Work through each Status Reason in order:
Draft: Click Transitions → add Submitted as a valid transition.
Submitted: Click Transitions → add Under Review and Withdrawn.
Under Review: Click Transitions → add Approved and Rejected.
Approved: Click Transitions → add Completed.
Rejected, Withdrawn, Completed: These are terminal states. Leave their transitions empty (no valid next transitions), since records should stop moving once they reach these states.
Save after each configuration. Once done, the system will enforce these paths.
Note
Transitions that cross from Active to Inactive Status Reasons (like going from "Approved" to "Completed") work the same way in the transitions UI — you just select the Inactive Status Reason value as a valid destination. Dataverse handles the parent Status change automatically.
This is where the configuration pays off visually. Open a Grant Application record in your model-driven app. You'll notice that the Status Reason field in the form header behaves differently now.
When a record is in Draft status, the Status Reason dropdown only shows Submitted as a valid option to change to. The user cannot set it to "Under Review" or "Approved" directly — those options simply don't appear. The form itself enforces the business rule.
Similarly, the Deactivate button in the command bar (which would set the record to Inactive) is suppressed when there's no valid Inactive transition from the current Status Reason. So from "Draft," users can't accidentally deactivate a record unless you've configured a transition path to an Inactive Status Reason.
If you're building a sophisticated user experience, you might pair this with business process flows that visually represent each stage as a BPF stage, giving users a progress bar across the top of the form while Status Reason transitions enforce the data integrity underneath.
Tip
You can also pair status transitions with business rules in Dataverse to show or hide specific fields depending on the current Status Reason. For example, you might show a "Rejection Notes" field only when Status Reason is "Rejected."
One area that confuses makers is how Activate and Deactivate interact with Status Reason transitions.
When a user clicks the Deactivate button in a model-driven app's command bar, Dataverse needs to know which Inactive Status Reason to assign. If you have only one Inactive Status Reason, it assigns that automatically. If you have multiple Inactive Status Reasons (like Rejected, Withdrawn, and Completed), Dataverse shows the user a dialog asking them to choose the specific Status Reason.
This dialog-based selection respects your transitions. If you're in "Approved" and your only configured transition to Inactive is "Completed," the user will only see "Completed" in that dialog. They won't accidentally mark an approved application as "Rejected."
Reactivating works the same way in reverse. If a user clicks Activate on an Inactive record, Dataverse asks which Active Status Reason to assign. With transitions configured, only valid Active transitions from the current Inactive Status Reason will appear.
Warning
By default, users with appropriate security privileges can activate and deactivate records. If your lifecycle design means certain records should never be reactivated (e.g., a "Completed" grant application should stay completed), enforce this with security role configuration to restrict the Activate privilege for specific record stages, or use a real-time workflow to block the operation.
When a new record is created, it needs a starting Status Reason. By default, the first Active Status Reason in the list is assigned. For your grant application, that should be "Draft."
To control which Status Reason is the default, open the Status Reason column and look for the Default value setting. Set it to Draft. This ensures every new grant application starts at the right point in the lifecycle without users having to manually set it.
You can also control the default programmatically if you're creating records via Power Automate or the Dataverse API, by explicitly setting the statuscode attribute value in your create operation. But for records created through the model-driven app form, the default value setting on the column is what drives this.
Once your Status Reason values are configured, they become tremendously useful for building targeted views. In your model-driven app, you can create views filtered to specific lifecycle stages — for example, a "Pending Review" view that shows only records in "Submitted" or "Under Review," or a "Closed Applications" view showing only Inactive records.
This kind of view segmentation is covered in depth in Creating and Customizing Views in Model-Driven Apps: Filters, Sorting, and Editable Grids. The key point here is that your carefully named Status Reason values become the filter criteria — so meaningful names matter.
You can also use Status Reason values in formula columns and rollup columns to calculate things like "count of applications currently under review" for dashboard metrics.
Work through this exercise in your own environment to cement the concepts.
Scenario: You're building a Support Ticket table for an internal helpdesk app. The ticket lifecycle is: New → Assigned → In Progress → Resolved (Inactive) or Closed (Inactive). Escalated is a valid step from "In Progress" back into "Assigned" (re-triaged). Tickets can also be Cancelled (Inactive) from New, Assigned, or In Progress.
Step 1: Create a custom table called Support Ticket if you don't already have one.
Step 2: Open the Status Reason column and add the following values:
Step 3: Enable Status Reason Transitions in the table properties.
Step 4: Configure these transitions:
New → Assigned, Cancelled
Assigned → In Progress, Cancelled
In Progress → Escalated, Resolved, Cancelled
Escalated → Assigned
Resolved → Closed
Closed → (none, terminal)
Cancelled → (none, terminal)
Step 5: Set the Default Status Reason to New.
Step 6: Build a simple model-driven app that includes the Support Ticket table. Open a new Support Ticket record and verify that the Status Reason dropdown only offers "Assigned" and "Cancelled" as next options. Change it to Assigned, then verify that "Escalated," "In Progress," and "Cancelled" appear as options — but "New" does not.
Step 7: Try deactivating a record that's in "In Progress" status. Verify the dialog shows Resolved and Cancelled (but not Closed, since Closed is only reachable from Resolved).
If the transitions are enforcing correctly, you've successfully built a governed record lifecycle.
"My transitions aren't showing in the form — the user still sees all Status Reason values."
The most common cause is forgetting to enable the transitions toggle at the table level before configuring individual transitions. Go back to Table Properties and confirm that Enable Status Reason Transitions is checked and saved. If it's off, Dataverse ignores all the transition configuration you've done on individual values.
"I configured transitions, but the Deactivate button isn't appearing when it should."
The Deactivate button's visibility in model-driven apps is controlled by both security privileges and the Status Reason transitions configuration. If the current Status Reason has no valid transition to any Inactive Status Reason, Dataverse hides the button. Double-check that you've added at least one Inactive Status Reason as a valid transition destination from the current Status Reason.
"A new record isn't starting at Draft — it's starting at 'Active' instead."
You forgot to set the Default Value on the Status Reason column. The system falls back to the first value in the list alphabetically if no default is specified. Set the explicit default.
"I can't find the Status Reason column on a standard table like Account."
Standard tables have Status Reason configured by Microsoft and the column may be locked or have different behavior. Focus your custom lifecycle design on custom tables where you have full control.
Warning
Transitions only govern changes made through the model-driven app UI and standard API calls that respect metadata. If someone updates a record's statuscode value directly via a bulk import or API call without checking transitions, those constraints can be bypassed. For compliance-critical scenarios, enforce the lifecycle in Power Automate flows or plugins as an additional layer on top of transitions.
"My Status Reason transitions look correct, but a Power Automate flow is ignoring them."
Correct — Power Automate (and direct Dataverse API calls) don't enforce Status Reason transition metadata by default. Transitions are a UI-layer guard. If you need server-side enforcement, you need a plugin or a real-time workflow that validates the transition server-side before the record saves.
You've covered a lot of ground. Here's what you now understand:
Status and Status Reason transitions are one of the most elegant tools in the Dataverse model-driven toolkit precisely because they put business logic into the data layer itself, not just in form behavior or app code. A record's lifecycle becomes a first-class part of your data model.
As a next step, explore how business process flows can overlay a visual stage-by-stage journey on top of your Status Reason transitions — they're complementary, not competing tools. BPFs show users where they are and guide them forward; Status Reason transitions enforce that they can't take invalid shortcuts. Together, they give you both great UX and solid data integrity.
You might also look at configuring Dataverse auditing and field-level change history to track every Status Reason change over time — a natural requirement in any governance-sensitive lifecycle application.