Most Business Process Flow implementations barely scratch the surface of what's possible. Learn how to build branching multi-path BPFs, enforce true stage gates at the data layer, scope stages to related Dataverse tables, and lock records when they reach completion — all in a realistic contract qualification scenario.
Your sales team closes deals through a consistent process, but your standard customers go through a 4-stage approval cycle while enterprise clients require compliance review, executive sign-off, and a legal hold step before the contract can move forward. You could try to squeeze both paths into a single linear Business Process Flow, littering it with "skip this if not enterprise" instructions in training docs nobody reads. Or you could model the actual process — branching at the point where the paths diverge, enforcing stage gates so nobody skips legal review on a $2M deal, and locking the record when it reaches completion.
That's what this lesson is about. Business Process Flows (BPFs) in Dataverse are more powerful than most practitioners realize. When you move beyond the basic linear flow and start working with branching conditions, table-scoped stages, required step enforcement, and completion locking, you end up with a genuine process control layer built into your model-driven app — not a series of checkbox fields your users can ignore.
By the end of this lesson, you'll be able to design and configure multi-path BPFs that fork based on data conditions, enforce stage gates that block progression until business criteria are met, scope stages to secondary tables, and wire up completion enforcement so that finished records can't drift back into work-in-progress states.
What you'll learn:
You should already be comfortable with the foundational BPF concepts covered in Business Process Flows: Guiding Users Through Multi-Stage Processes in Power Apps. You should also understand Dataverse Fundamentals: Tables, Columns, and Rows Explained for Power Apps Makers and have hands-on experience configuring model-driven app forms. Familiarity with Dataverse table relationships will help when we get to table-scoped stages.
Before you touch the BPF designer, you need to understand what a BPF actually is at the data layer — because this knowledge will save you enormous debugging headaches later.
When you create a BPF and activate it, Dataverse creates a dedicated BPF table behind the scenes. For example, a BPF named "Deal Qualification Process" generates a table called new_dealqualificationprocess. Every time a qualifying record triggers the BPF, a new row is created in that BPF table. That row tracks the current stage, the active path (in a branching BPF), and the traversal history.
This matters for several reasons:
Key insight
The BPF process bar users see on the form is a UI projection of a row in the BPF table. Understanding this separation explains why changes to BPF configuration don't automatically retroactively update active instances — and why you sometimes need to migrate instances manually after redesigning a flow.
The primary table (your Opportunity, Case, or custom entity) gains two system columns when you associate a BPF: processid (which BPF definition is active) and stageid (which stage the record currently sits in). You can use these columns in views, charts, and business rules to surface process state throughout your app.
For this lesson, we'll build a Contract Qualification Process for a professional services company. Here's the business requirement:
The primary table is Contract (custom table). We'll also scope the Scoping stage to pull steps from a related Scope of Work table.
This is a realistic scenario. The branching condition is data-driven (contract value + tier). The stage gates involve field requirements and document presence. The completion enforcement prevents revision gaming.
Your BPF conditions are only as good as the data columns driving them. Before opening the BPF designer, ensure your Contract table has these columns configured:
| Column Display Name | Schema Name | Type | Notes |
|---|---|---|---|
| Estimated Value | cr7f2_estimatedvalue |
Currency | Required for branching condition |
| Customer Tier | cr7f2_customertier |
Choice | Options: Standard, Enterprise |
| Scoping Document | cr7f2_scopingdocument |
File or URL | Required step in Scoping stage |
| Compliance Approved | cr7f2_complianceapproved |
Yes/No | Set during Compliance Review stage |
| Executive Approval Date | cr7f2_executiveapprovaldate |
Date Only | Set during Executive Sign-Off stage |
| Contract Status | cr7f2_contractstatus |
Choice | Options: Draft, In Review, Approved, Rejected |
If you're not sure how to configure these column types and their required-field settings, the Configuring Model-Driven App Column Properties: Data Types, Required Fields, and Display Settings for Dataverse Tables article covers this in depth.
Tip
Choice columns (formerly Option Sets) are your best friend for BPF branching conditions. They constrain the possible values, making condition logic deterministic. Avoid free-text fields as branching conditions — a typo in "Enterprise" will silently route records down the wrong path.
Navigate to Power Apps > Solutions > [your solution] > New > Automation > Process > Business Process Flow. Give it a meaningful name: Contract Qualification Process. Select Contract as the table.
The BPF designer opens with a single default stage. Here's how to build the initial linear portion:
Click the default stage and rename it Qualification. Set the Category to Qualify — this is a display hint that appears in the process bar.
Add the following steps to this stage:
Customer Tier — mark as RequiredEstimated Value — mark as RequiredContract Status — mark as Required, with display name "Set Initial Status"Required steps in the BPF designer don't use the same enforcement mechanism as required columns on forms. A BPF required step means the user must interact with (populate) that field before clicking Next Stage. This is a softer gate than a form required field — it checks for a non-null value at the moment of stage progression, not on save.
Warning
BPF required steps only enforce when progressing forward via the BPF bar. A user who edits the field directly on the form and saves can leave it blank without the BPF blocking them. For hard enforcement at save time, combine BPF required steps with Business Rules in Dataverse: Validation and Field Logic Without Code set to the "Entity" scope, which fires regardless of BPF state.
Add a second stage. Name it Scoping. Set Category to Develop.
Steps:
Scoping Document — RequiredEstimated Value — Required (confirm or update value after scoping)We'll come back to the table-scoping configuration for this stage in the next section.
Add a third stage. Name it Approval. Set Category to Close.
Steps:
Contract Status — Required, with instruction text "Set to Approved or Rejected"At this point you have a working 3-stage linear BPF. Activate it and test it before adding branching — this saves you from debugging two complex things simultaneously.
Here's a feature that's underused: individual BPF stages can be bound to a related table rather than the primary table. This means the steps in that stage read from and write to a related record, while the BPF itself remains anchored to the primary Contract record.
This is powerful when your process involves populating a different entity's data as a process milestone. In our scenario, the Scoping stage is a natural fit — the Scope of Work is a child record of Contract.
Your Scope of Work table needs a many-to-one lookup to Contract — one contract, potentially multiple scopes, but the BPF will interact with the first or active one. Make sure this relationship exists before configuring the stage. For a refresher on setting up these relationships, see Configuring Dataverse Table Relationships in Model-Driven Apps: One-to-Many, Many-to-Many, and Cascade Behaviors Explained.
In the Scoping stage panel (click the stage, then look at the right panel), find the Entity dropdown. By default it shows Contract. Change this to Scope of Work.
When you do this, the available data columns in the step editor switch to columns from the Scope of Work table. You can now add steps like:
sow_description (Description) — Requiredsow_deliverables (Deliverables List) — Requiredsow_estimatedhours (Estimated Hours) — RequiredNote
When a BPF stage is table-scoped to a child entity, the platform navigates to the related record automatically when the user's process bar hits that stage. If no related record exists yet, Dataverse creates one. This automatic creation uses the relationship to populate the lookup back to the parent — which is usually exactly what you want, but means your relationship configuration must be correct before deployment.
The user experience here is seamless from the UI perspective — the process bar on the Contract form still shows all stages, but when the user fills in Scoping steps, they're writing to the Scope of Work table. This is an elegant way to gather structured data from related entities without forcing users to navigate away from the parent record.
Now for the centerpiece. After the Scoping stage, we need the flow to fork: standard contracts go straight to Approval, enterprise/high-value contracts go through the compliance path.
In the BPF designer, look for the Condition element in the Components panel (on the left side of the canvas). Drag a Condition component onto the canvas and drop it after the Scoping stage. You should see a diamond-shaped condition node appear on the canvas.
Click the condition node. The panel on the right lets you define condition rules. These work similarly to view filters — you select a column, an operator, and a value.
Set up the condition with an OR logic:
Rule 1: Estimated Value (Contract) Is Greater Than Or Equal To 100000
[OR]
Rule 2: Customer Tier (Contract) Equals Enterprise
The condition node will now have two outbound paths:
On the Yes path, drag three new stages from the Components panel:
Stage: Compliance Review
Compliance Approved (Yes/No) — Requiredcr7f2_compliancenotes (Multi-line text) — OptionalStage: Executive Sign-Off
Executive Approval Date — Requiredcr7f2_executivename (Single line of text) — RequiredStage: Legal Hold
cr7f2_legalholddocument (File/URL) — Requiredcr7f2_legalclearancedate (Date) — RequiredAfter Legal Hold, connect the path back to the Approval stage. In the BPF designer, you do this by dragging an arrow from Legal Hold to the Approval stage. Both the Yes path and the No path should now terminate at Approval — the stage is shared.
Key insight
Branches in a BPF can converge at a single shared final stage. This convergence is configured visually in the designer by connecting both branch endpoints to the same downstream stage node. However, you cannot have a branch path loop back to an earlier stage — BPF branches always move forward.
The No path from the condition node connects directly to Approval. Drag the No path arrow to the Approval stage. This completes the fork-and-rejoin structure.
Your canvas should now look like:
[Qualification] → [Scoping] → ◇ Condition
├── Yes → [Compliance Review] → [Executive Sign-Off] → [Legal Hold] → [Approval]
└── No ──────────────────────────────────────────────────────────────────→ [Approval]
Before activating, save the BPF and use the Validate button in the toolbar. The validator will flag any disconnected paths, missing required configurations, or circular references. Fix all warnings before activation.
Once validated, click Activate. The BPF is now live for new Contract records.
When a Contract record is created, the BPF instance starts at Qualification. As the user advances through Scoping, they eventually hit the condition node. Here's what Dataverse does:
new_contractqualificationprocess table.This evaluation timing is important. If a user sets Estimated Value to $80,000 during Qualification but then updates it to $150,000 during Scoping, the branch evaluates against the $150,000 value when they click Next Stage from Scoping. This is typically the desired behavior — you want the most current data driving the routing decision.
Warning
If your condition column is blank when the user attempts to advance past the condition stage, Dataverse treats the blank as a non-matching condition and routes down the No path. Make sure the columns used in your branch conditions are marked as Required steps in the preceding stage to prevent silent misrouting due to missing data.
BPF required steps are a start, but serious stage gating requires combining multiple enforcement mechanisms. Here's a layered approach that works in production.
As configured above — these block the "Next Stage" button if the field is null when the user clicks it. Simple, built-in, zero code.
A Business Rule scoped to Entity (not Form) fires on every save, regardless of how the data changes. Set up a rule that:
You can find the stage GUID from the BPF designer URL or by querying the processstage table in the maker portal. This is admittedly clunky, but it creates hard enforcement that survives even direct API edits.
For the strictest enforcement — particularly for the Legal Hold stage where compliance matters — consider a real-time workflow or plugin that fires on the Contract update event and checks:
Contract Status is being set to Approvedcr7f2_legalclearancedate is null (for enterprise-path records)This approach is covered in detail in Configuring Dataverse Table Event Plugins and Real-Time Workflows in Model-Driven Apps: Enforcing Complex Business Logic at the Data Layer Without Custom Code. The BPF step gates the UI; the plugin gates the data layer. Together they're genuinely robust.
Control which security roles can edit which columns at which stages. Users in the "Sales Rep" role shouldn't be able to set Compliance Approved to Yes — that should only be available to "Compliance Officer" roles. Column-level security on sensitive fields is your mechanism here. This ensures stage gating isn't just a UI convenience but an access-controlled data policy.
One of the most common gaps in BPF implementations is what happens when a record reaches the final stage. By default, nothing locks it — users can click back to earlier stages, change data, and re-advance. For a sales opportunity, that might be fine. For a compliance-controlled contract, it's a problem.
Create a business rule that fires when Contract Status equals Approved:
This is a form-level lock — it prevents UI edits but doesn't block API access. For most operational scenarios, it's sufficient.
The BPF itself has a Finish state. When a user clicks "Finish" on the final stage, the BPF instance status changes to Finished and the process bar collapses. You can configure your process so Finish is only available when Contract Status equals Approved.
Do this by adding a required step in the Approval stage for the Contract Status column and using a business rule that prevents saving with Approved status unless prior conditions are met. The combination means: you can't set Approved without conditions met, and you can't Finish the BPF without setting Approved.
Create a cloud flow triggered on Dataverse row updated for the Contract table. When Contract Status changes to Approved:
new_contractqualificationprocess tablestatecode to 1 (Inactive) and statuscode to 2 (Finished)OwnerId to a locked service account so regular users can no longer edit itThis programmatic completion is cleaner than relying on users to manually click Finish, and it runs regardless of whether the user exits the form after saving.
Tip
When you programmatically finish a BPF instance via Power Automate or API, the process bar disappears from the record form. If you want the completed stage history to remain visible as a read-only audit trail, consider writing the final stage name and completion timestamp to dedicated text/date columns on the Contract record at the same time you finish the instance. Users lose the visual bar but gain a permanent record stamp.
A Contract record can have multiple BPF definitions associated with the same table. If your organization has both a "Contract Qualification Process" and a "Contract Renewal Process," both appear as options when users create a new contract.
In the BPF list view (Solutions > Processes), you can set the Order of BPFs for a given table. The lowest-order BPF is applied automatically when a new record is created. Users with appropriate privileges can switch the active BPF from the process bar switcher.
Note
When a user switches the active BPF on a record, the previous BPF instance is abandoned (set to Inactive) and a new instance is created for the newly selected BPF starting at its first stage. Data already entered on the record is not lost — only the process tracking resets. This can confuse users who switch accidentally, so consider whether your security configuration should restrict the ability to switch BPFs to specific roles.
Each BPF table generated by the system needs to be explicitly included in security role configurations. Navigate to Security Roles and find the BPF table (it appears in the Custom Entities or Process tables section). Give the appropriate roles Create, Read, Write, and Append privileges on the BPF table. Without this, users will see the form but the process bar will be missing or broken.
This is part of the broader model-driven app security configuration covered in Model-Driven App Security: Configuring Security Roles, Field Permissions, and Team-Based Access for Table Data.
One of the most practical wins from a well-structured BPF is the ability to filter and group records by stage — giving managers real pipeline visibility.
The Contract table's Stage Name virtual column (sourced from the active BPF instance) can be added to views. Navigate to your Contract view configuration and add the Process Stage column. You can then filter or group the view by stage name.
For more control, use the stageid column and a lookup to the Process Stage table to get the stage name text. This works better in advanced filters where you want to query by a specific stage GUID.
If you want to go further with view configuration for surfacing BPF-stage-filtered records, Configuring Model-Driven App Views as Default Views, Quick Find Views, and Lookup Views: Controlling How Records Are Surfaced Across the App has deep coverage of view filter configuration.
Once you know a record's current stage, you can build formula columns that derive useful metrics — for example, a "Days in Current Stage" column that calculates the difference between today and the date the record entered the stage. This requires either a calculated column using the stage transition date (which you'd write via Power Automate on stage change) or a formula column using the stored timestamp.
See Formula Columns and Rollup Columns in Dataverse: Calculated Data Without Code for the mechanics of building these derived calculations.
Build the full Contract Qualification Process described in this lesson from scratch in a development environment. Follow these steps:
Step 1 — Table Setup
Create a Contract custom table with all columns listed in the table earlier in this article. Ensure Estimated Value is a Currency column and Customer Tier is a Choice column with Standard and Enterprise options.
Step 2 — Scope of Work Table
Create a Scope of Work table with columns: Description (Multiline Text), Deliverables List (Multiline Text), Estimated Hours (Whole Number). Add a Many-to-One lookup from Scope of Work to Contract.
Step 3 — Build the BPF
Create the BPF with all stages as specified. Configure the Scoping stage to be scoped to the Scope of Work table. Add the condition node after Scoping with the OR condition (value ≥ 100,000 OR tier = Enterprise).
Step 4 — Required Steps and Stage Gates Add required steps to each stage. Then create at least one Business Rule at entity scope that enforces that Scoping Document cannot be blank when Contract Status is not Draft.
Step 5 — Completion Enforcement
Build a Power Automate cloud flow that triggers on Contract row update. When Contract Status changes to Approved, retrieve the BPF instance and set it to Finished. Add a column Approval Completed On (Date) and stamp it with today's date in the same flow.
Step 6 — Test Both Paths Create two contracts:
Verify that attempting to skip required steps blocks stage progression. Verify that completing Contract B all the way through finishes the BPF instance and stamps the completion date.
Cause: The condition node wasn't connected to separate downstream stages before saving. The designer can sometimes silently default both paths to the same connection.
Fix: After placing the condition, explicitly drag the Yes and No arrows to their respective destinations. Save, then re-open to verify both arrows are correctly routed. Use the Validate function before activating.
Cause: The automatically generated BPF table is missing from those users' security roles.
Fix: Open Security Roles, find the BPF table (search by your BPF name in the Custom Entities section), and grant Read + Write + Create + Append privileges at the User level minimum.
Cause: The column used in the condition was null at branch evaluation time, causing all records to take the No path.
Fix: Make the branching condition columns Required steps in the preceding stage. Also add an entity-scoped business rule that prevents saving without these values when the record is in the Scoping stage.
Cause: User accidentally switched to a different BPF, resetting the process bar while keeping field data.
Fix: In the security role configuration, remove "Write" on the BPF table for roles that shouldn't be able to switch flows. Or use a business rule to make the Switch BPF option unavailable by controlling the activestageid column through locking logic.
Cause: When the BPF transitions back to a table-scoped stage (after a backward move in testing), it may create a second Scope of Work record instead of referencing the existing one.
Fix: Never allow backward stage movement on table-scoped stages in production. Enforce this by locking backward stage navigation for users who have progressed past the table-scoped stage. Use a plugin or real-time workflow to prevent backward stage moves on the primary table.
Cause: Power Automate updated the BPF instance statecode but the form cached the previous BPF bar state.
Fix: Add a form refresh trigger. In your Power Automate flow, after updating the BPF instance, also update a date-time column on the Contract record (like Approval Completed On) which forces the form to refresh on next open, picking up the finished BPF state.
You now have the full blueprint for building production-grade Business Process Flows that go well beyond a simple stage checklist. The key architectural principles from this lesson:
Where to go from here: if your process logic is becoming too complex for BPF branching alone — particularly if you need conditional field visibility, cross-table calculations, or enriched form behavior that changes by stage — investigate combining your BPF with Designing Model-Driven Forms: Sections, Tabs, Subgrids, and Quick View Forms to control what users see at each stage via business rule-driven tab/section visibility.
If compliance and audit tracking of stage transitions matters for your organization, layer on the capabilities from Configuring Dataverse Auditing and Field-Level Change History in Model-Driven Apps — you'll be able to produce a full timeline of every stage transition, who triggered it, and what data was present at each gate.