Standard lookups can't capture the full complexity of who plays what role in a relationship — a contact might be a sponsor on one project and a stakeholder on another. This lesson teaches you how to design, configure, and deploy Dataverse connection roles for real-world professional scenarios, including reciprocal pairs, table restrictions, form surfaces, and security.

Imagine you're building a CRM-style app for a consulting firm. You have contacts, accounts, and projects — but the relationships between them aren't just structural foreign keys. A contact might be a "Sponsor" on one project, a "Technical Reviewer" on another, and the "Primary Point of Contact" at an account. Another contact might serve as "Legal Counsel" for multiple accounts simultaneously. Standard one-to-many relationships with lookup columns can't express this richness — they'd require a separate lookup column for every possible role, and that falls apart quickly when roles are dynamic or a single contact plays multiple roles on the same record.
This is exactly the problem that Dataverse Connection Roles solve. Connections are a native Dataverse mechanism for creating named, typed, bidirectional associations between records — including records from different tables. Rather than building custom junction tables for every "person plays role on project" scenario you encounter, you configure connection roles that users can apply at runtime, describe the nature of the relationship, and surface in subgrids on any form. By the end of this lesson, you'll have a fully working connection role configuration for a realistic professional services scenario, and you'll understand the design decisions that make connections the right tool in some situations — and the wrong tool in others.
What you'll learn:
You should be comfortable with Dataverse table relationships and know how lookups and many-to-many relationships work — if you need a refresher, Designing a Dataverse Data Model: Relationships, Lookups, and Choice Columns covers that foundation. You should also have a working model-driven app to configure, and access to the Power Apps maker portal (make.powerapps.com) with a System Customizer or System Administrator security role in your environment.
Before you touch the maker portal, let's build a clear mental model of how connections work under the hood. This matters because the UI presents connections in a slightly abstracted way that can be confusing if you don't understand what's actually being stored.
Every connection you create in Dataverse is a record in the system connection table. Each connection record holds:
record1id + record1objecttypecode)record2id + record2objecttypecode)connectionrole record describing the role Record 1 playsconnectionrole record describing the role Record 2 playsThe connectionrole table stores the role definitions you configure — things like "Project Sponsor," "Legal Counsel," or "Technical Reviewer." Each role record belongs to a Connection Role Category (a global option set), which gives you a way to group and filter roles in the UI.
Key insight
A single connection record captures a bidirectional relationship. When a contact is the "Sponsor" of a project, one connection record exists. The connection role for Record 1 is "Sponsor" and the connection role for Record 2 might be "Has Sponsor" — or it might be left unspecified. You don't need two records pointing in opposite directions.
This is the design decision you'll face most often. Standard relationships (the kind covered in Configuring Dataverse Table Relationships in Model-Driven Apps: One-to-Many, Many-to-Many, and Cascade Behaviors Explained) are structural — they live in the schema, support cascade behaviors, and enable rollup columns, calculated fields, and filtered views. Connections are runtime data — they're flexible but can't participate in rollups, formula columns, or cascade deletes in the same way.
Use connections when:
Use standard relationships when:
In practice, well-designed Dataverse apps use both: structural lookups for the core data model, and connections for the soft "who plays what role" associations that business users define and manage over time.
Let's break down what you're actually configuring when you create a connection role.
Every role must belong to a category. The Connection Role Category is a system option set (not customizable beyond adding values through solution customization) with these built-in values:
| Category Value | Typical Use |
|---|---|
| Business | Professional or organizational relationships |
| Family | Personal relationships (used in nonprofit/social sector apps) |
| Social | Informal or community relationships |
| Other | Catch-all |
| Sales Team | Relationships related to a sales team member |
| Stakeholder | Project or initiative stakeholders |
You can add custom option set values here, but most professional apps work fine with Business or Stakeholder. Categories primarily control filtering in the connection role picker — they don't enforce anything structurally.
Connection roles can (and usually should) be paired. A "Project Sponsor" role has a natural counterpart: the project being sponsored might be described as "Sponsored Project" from the contact's perspective. This pairing is called a reciprocal role.
Reciprocal roles are important because connections are bidirectional records. When you look at a contact and see their connections, you want to see that they're a "Sponsor" of Project Alpha — not just see a raw connection record. When you look at Project Alpha, you want to see that it "Has Sponsor" — not the same label from both ends.
Note
Reciprocal roles are optional in the schema — you can create a connection role with no reciprocal. But in practice, skipping reciprocal roles leads to a confusing user experience where the relationship reads the same from both ends, which rarely makes semantic sense.
By default, a connection role can be used with any table that has connections enabled. This sounds flexible, but it creates data quality problems: users can accidentally assign "Project Sponsor" as the role for a connection between two accounts, which is probably not what you intended.
Every production connection role should have explicit table restrictions. You restrict a role by associating it with specific tables — only records from those tables will be eligible for that role on a connection.
Before you can create connections between records, connections must be enabled on the participating tables. For standard system tables (Contact, Account, Lead, Opportunity, etc.), connections are already enabled. For custom tables, you need to turn this on.
Navigate to make.powerapps.com, open your solution, find your custom table, and open its settings. Under the General tab, look for the Connections checkbox in the collaboration section and ensure it's checked. Save and publish.
Warning
You cannot disable connections on a table after enabling it if connection records already exist for that table. Plan your table configuration before going into production. Enabling connections is a one-way door once data exists.
For our professional services example, we'll be working with:
Before creating individual roles, invest time in designing your taxonomy. Ad-hoc connection roles created by different team members over time become a mess of overlapping, inconsistently named roles that confuse users. Design first, build second.
We're building a project management app for a consulting firm. The business needs to track:
This gives us a clean set of role pairs to design:
| Role Name | Category | Reciprocal Role | Table 1 | Table 2 |
|---|---|---|---|---|
| Project Sponsor | Stakeholder | Sponsored Project | Contact | Project |
| Project Stakeholder | Stakeholder | Has Stakeholder | Contact | Project |
| Legal Counsel | Business | Represented By | Contact | Account |
| Legal Client | Business | Provides Counsel To | Account | Contact |
| Reports To | Business | Manages | Contact | Contact |
| Manages | Business | Reports To | Contact | Contact |
| Secondary Affiliation | Business | Affiliated Contact | Account | Contact |
Notice a few design choices here:
You create connection roles through the classic interface in the Power Apps maker portal. The modern solution explorer doesn't yet have a full connection role editor, so you'll navigate through the legacy path.
Alternatively, from the maker portal, go to your solution and look for Connection Roles in the left panel under the Objects section. If it's not visible, use Add Existing to find and add existing connection roles, or create new ones from the Advanced Settings path.
Let's walk through creating the first role in detail. The rest follow the same pattern.
Project SponsorStakeholderThe client-side executive who champions and sponsors this project engagement. Good descriptions matter — users see them in the connection role picker.With the Project Sponsor role record open:
This means "Project Sponsor" can only be the role for a Contact record when that contact is connected to a Project record. The system won't allow a user to assign "Project Sponsor" to an Account-to-Account connection.
Tip
If you want a role to apply to a single table type on one side but any table on the other side, only add one table restriction. If you add two table restrictions (Contact and Project), the system allows the role only when one record is a Contact AND the other is a Project. Adding only Contact allows the role whenever a Contact is on either end of the connection, regardless of what's on the other end.
StakeholderA project for which this contact serves as the client-side sponsor.Now go back to the Project Sponsor role record:
Then open the Sponsored Project role and set its reciprocal to Project Sponsor. The relationship is bidirectional — both roles need to point to each other.
Warning
If you set the reciprocal only on one side, the connection UI will work for record creation, but the display from the other record's perspective will not show the correct role label. Always set reciprocals on both role records.
Following the same process, create all the roles from your taxonomy table. Pay particular attention to the "Reports To" / "Manages" pair — since both roles involve Contact-to-Contact connections, each role should have only one table restriction: Contact.
With roles configured, let's create actual connections in the app. Understanding the end-user experience helps you design better forms and troubleshoot issues.
In a model-driven app, connections appear in the Connections subgrid, which is a standard system subgrid available on any form for a table with connections enabled. If you followed the guidance in Designing Model-Driven Forms: Sections, Tabs, Subgrids, and Quick View Forms, you know how to add subgrids to forms.
To add the Connections subgrid to your Project form:
When a user opens a Project record and clicks the + button in the Connections subgrid, the connection dialog opens. The user selects:
After saving, the connection appears in the subgrid on both the Project record and the Contact record.
Key insight
The connection subgrid on Sarah Chen's Contact record will show "Sponsored Project" as her role label — not "Project Sponsor." This is the reciprocal role at work. From Sarah's record, you're reading the relationship from her perspective: she is not the "Project Sponsor" of herself, she is the person who is the sponsor, and the project is her "Sponsored Project." This bidirectional labeling is what makes connections semantically rich.
The Connections subgrid shows connections for a single record. But you'll often need cross-record views: "Show me all contacts who are Project Sponsors across all active projects" or "Show me all projects where we have no named sponsor."
Because the connection table is a standard Dataverse table, you can query it in Advanced Find and build views on it. Navigate to Advanced Find in your model-driven app, and choose Connections as the primary table. You can then filter on:
Record 1 Role (Connection Role) = Project SponsorRecord 2 (Project) Status = ActiveThis gives you a flat list of all Project Sponsor connections. You can add columns for the connected record names and export or surface this in a dashboard.
Note
The connection table uses polymorphic lookup columns (record1id, record2id) that reference different table types. This means you can filter on the connection role to constrain which record types appear, but you can't do a native join to pull in columns from the connected record (like Project Budget or Contact Email) directly in the view. For richer reporting, use a Power BI dataset or a Dataverse Dataflow that joins the connection table to the relevant entity tables. See Formula Columns and Rollup Columns in Dataverse: Calculated Data Without Code for context on what's possible inside the platform.
You can also query connections programmatically. In a Power Automate flow using the Dataverse connector, use List rows on the connections table with a filter like:
record1roleid/connectionroleid eq 'YOUR-CONNECTION-ROLE-GUID'
and statecode eq 0
Replace YOUR-CONNECTION-ROLE-GUID with the GUID of your Project Sponsor connection role (find it in the URL when you open the role record in the legacy interface). This returns all active connections where Record 1 is playing the Project Sponsor role.
To get the GUID cleanly, open the connection role in the Advanced Settings interface and look at the browser URL — it contains the connection role ID in standard GUID format.
Raw connection subgrids show everything — every connection on the record, regardless of role. In a complex app, this becomes overwhelming. You want users to see structured, role-specific panels, not a generic list of associations.
The Connections subgrid supports filtering, but the native filter doesn't easily filter by connection role in the standard form editor. A practical workaround is to create custom views on the Connection table that pre-filter by role:
Record 1 Role = Project SponsorThis subgrid now shows only the Project Sponsor connections for the current record, giving users a clean "Sponsors" panel rather than a cluttered catch-all list.
You can add multiple such subgrids to a single form — one for Sponsors, one for Stakeholders — each filtered to its relevant role. If you're building a detailed relationship management tab, this approach creates a structured, readable layout that mirrors real business concepts. Review the guidance in Designing Model-Driven Forms: Sections, Tabs, Subgrids, and Quick View Forms for the mechanics of tab and subgrid layout.
Tip
Label each subgrid section clearly with a section header that matches the role — "Project Sponsors," "Stakeholders," "Legal Counsel" — so the form reads like a structured relationship directory rather than a generic list. Users shouldn't need to interpret connection role codes to understand what they're looking at.
The Timeline control on forms automatically logs connection creation events as activities. This means when a new sponsor is added to a project, that association appears in the timeline with a timestamp and the user who created it. For audit-conscious clients, this is valuable — it gives you a record of when relationships were established without any custom configuration.
Connections introduce a non-obvious security dimension: users need privileges on both the connection table and the connectionrole table to create and manage connections. Out-of-the-box security roles include connection privileges, but custom security roles often miss them.
For a user to create connections, they need:
| Table | Privilege | Minimum Level |
|---|---|---|
| Connection | Create | User |
| Connection | Read | User |
| Connection | Write | User |
| Connection | Delete | User |
| Connection Role | Read | Organization |
| Connection Role | Append | Organization |
| Connection Role | Append To | Organization |
The Connection Role table requires Organization level read because roles are shared across the entire environment — they're not owned by individual users or business units. If a user has only User-level read on Connection Roles, they won't see any roles in the connection picker.
Warning
A common mistake in custom security roles is granting connection create/read/write but forgetting to grant Connection Role read at Organization level. The result: users can open the connection dialog but see an empty role picker. They create connections with no roles, which defeats the purpose entirely. Always test your security configuration with a non-admin user account before going live. See Dataverse Security: Business Units, Security Roles, and Teams for the broader security model context.
Connection role records themselves (the definitions, not the connections) can only be created and modified by users with the System Customizer or System Administrator security role. End users cannot create new connection role types at runtime — they can only use the roles you've defined. This is appropriate: you don't want users inventing ad-hoc role types that undermine your taxonomy.
Let's put this all together. You'll build the complete connection role configuration for the professional services scenario and wire it into a model-driven app form.
Following the taxonomy table from earlier, create all seven role pairs. For each pair:
Work through the pairs in this order (it's easier to link reciprocals if both exist first):
This ordering avoids the frustration of trying to set a reciprocal to a role that doesn't exist yet.
Open a Contact record (create a test contact if needed — "Sarah Chen, Project Consultant"). Open the Connections subgrid on the Contact form (it should be there by default on system Contact forms). Create a connection:
Save and verify the connection appears in the subgrid on both the Contact and Project records, with the correct role label on each end.
Then in Advanced Find, build a Project Sponsors view on the Connection table filtered by the Project Sponsor connection role, save it, and update the subgrid to use it as the default view.
Create a test security role that mirrors what your app users will have. Ensure it includes:
Assign this role to a test user and confirm they can create connections with roles using your app.
Symptom: When you view a connection from Record 2's perspective, the role label is blank or shows the wrong name.
Fix: Open both connection role records and ensure each one's Reciprocal Connection Role field points to the other. The linkage must exist on both sides.
Symptom: Users can assign "Project Sponsor" to Account-to-Account connections, creating nonsensical data.
Fix: Add explicit table restrictions on every production connection role. Audit existing roles using Advanced Find on the Connection Role table and check which ones have no associated Record Types.
Symptom: Users open the connection dialog and see no roles in the dropdown.
Fix: Check the user's security role for Connection Role: Read at Organization level. Also verify that your connection roles are in an Active state (check the status in the connection role list view).
Symptom: You created a connection, but the subgrid on the second record shows nothing.
Fix: Verify connections are enabled on that table. Then check whether the subgrid's view is filtering in a way that excludes the record. The default "All Connections" view should show both sides, but if you've customized the view's filters, double-check the filter logic.
Symptom: You created connection roles in your dev environment, but they don't appear in the target environment after solution import.
Fix: Connection roles must be explicitly added to your solution. In the solution editor, go to Add Existing > Connection Role and add all the roles your app depends on. They are not automatically included just because you created them while working in the solution context.
Warning
This is one of the most common deployment failures with connection roles. Unlike table relationships and columns, connection roles live at the environment level and don't automatically travel with your solution. Always add them to your solution explicitly and test a full import to a clean environment before going to production. The guidance in Solutions for Model-Driven Apps: Publishers, Managed vs Unmanaged, and Solution Layering covers solution packaging best practices that apply here.
Symptom: You delete a Project record and expect all its connection records to delete automatically.
Fix: Standard cascade delete does not apply to connections the same way it does to standard relationships. You'll need a Power Automate flow or a plugin to clean up connection records when the primary record is deleted, if that's a business requirement. Plan for this explicitly in your data lifecycle design.
Dataverse connection roles give you a flexible, user-managed way to model party-to-party associations that don't fit neatly into the fixed-schema world of standard table relationships. The core pattern is clear: define role pairs with meaningful names, link reciprocals, restrict to appropriate tables, surface through filtered subgrids, and secure with correct privileges on both the Connection and Connection Role tables.
The professional services scenario we worked through — project sponsors, stakeholders, legal counsel, and reporting relationships — illustrates the kind of real-world complexity that connections are designed to handle. A contact can be a sponsor on one project and a stakeholder on another; an account can have legal counsel from an external firm; contacts can have management relationships that are tracked separately from their organizational hierarchy. All of this lives in runtime data rather than schema, which means your data model stays clean and your users can evolve the relationship landscape without needing a developer.
Where to go from here:
Connection roles are one of those features that experienced makers often overlook in favor of building custom junction tables, but once you've used them correctly, you'll recognize how much unnecessary complexity they can eliminate from your data model.
Model-Driven Apps & Dataverse