Connect Power BI semantic models to Bag of words and query them with DAX
Bag of words connects to the Power BI REST API to auto-discover the workspaces, semantic models (datasets), tables, and reports an identity can access, and to run DAX queries against them. This page covers everything you set up on the Microsoft side (app registration, tenant settings, workspace membership, permissions) and how to enter it in Bag of words.
The Power BI connector is part of the Enterprise Edition. Per-user (delegated) authentication in particular requires an Enterprise license.
Bag of words authenticates to Power BI as an Azure AD (Microsoft Entra ID) application, then:
Lists the workspaces the identity belongs to (GET /v1.0/myorg/groups).
Lists the semantic models (datasets) and reports in each workspace.
Reads each model’s tables and columns so the AI agent knows the schema. Discovery uses the read-only Admin (Scanner) API when available, and falls back to a DAX COLUMNSTATISTICS() probe per model otherwise.
Runs DAX queries at request time (POST .../executeQueries).
Each Power BI table is exposed to the agent as a schema table named Dataset/Table (for example Sales Model/Customers).
Power BI supports two authentication modes. You can use either, or both.
Service Principal (Azure AD)
A single shared application identity (a system credential) discovers and queries Power BI for everyone. Simplest to operate. Best when all users may see the same models.
Sign in with Microsoft (per-user)
Each user signs in with their own Microsoft account (OAuth / on-behalf-of). Discovery and queries run with that user’s own permissions, so each person sees only what they are allowed to. Requires Enterprise — see Authentication.
With per-user authentication, a semantic model is selectable for a user as long as their own account can see it — even if the shared service principal cannot. Usage and instructions are still tracked at the organization level per model.
In the Azure Portal → Microsoft Entra ID → App registrations → New registration.
1
Create the app registration
Give it a name (e.g. Bag of words - Power BI). Under Supported account types, choose Accounts in this organizational directory only unless you specifically need multi-tenant.
2
Copy the Tenant ID and Client ID
From the app’s Overview page, copy the Directory (tenant) ID and the Application (client) ID. You will paste these into Bag of words.
3
Create a client secret
Go to Certificates & secrets → New client secret. Copy the secret value immediately (it is only shown once). This is the client_secret field in Bag of words.
4
Add API permissions
Under API permissions → Add a permission → Power BI Service, add the delegated Tenant.Read.All (or the read scopes your policy allows). For per-user sign-in, keep the delegated profile, email, and openid scopes. Grant admin consent for the directory.
5
(Per-user only) Add the redirect URI
For Sign in with Microsoft, add a Web redirect URI under Authentication:
These live in the Power BI / Fabric Admin portal → Tenant settings (a Fabric/Power BI administrator must change them). Applying each to a dedicated security group and adding your app to that group is the recommended pattern.
Tenant setting
Why it matters
Service principals can use Fabric / Power BI APIs
Required for the service principal to call the Power BI REST API at all. Without it, discovery and queries fail.
Service principals can access read-only admin APIs
Lets Bag of words read model schemas via the Admin (Scanner) API — the most reliable discovery path, and the only one that reads schema without per-model query permission.
Enhanced admin API responses with detailed metadata
Makes the Scanner API return table/column schema (datasetSchema). Strongly recommended, especially for DirectLake and Fabric default semantic models.
If the read-only admin APIs are not enabled, Bag of words falls back to a per-model DAX probe. That probe requires Build permission on each model and does not work for some DirectLake models — which is the most common reason a model appears in Power BI but is missing from the schema list. Enabling the two admin settings above resolves this for most tenants.
After enabling Enhanced admin API responses, models that have not been refreshed since the setting was turned on may still return no schema until their next refresh. Refresh such models once.
For the service principal, discovery only sees workspaces where it holds a workspace role — sharing a single report or dataset directly is not enough. With per-user authentication this is looser: a model shared directly with a user (Build permission) is also discovered, even when they have no workspace role at all. That item-level path is exactly what row-level security requires (see the RLS step).
1
Add the identity as a workspace Member or Contributor
In each Power BI workspace → Manage access → Add people or groups, add the service principal (or the security group containing it), and any per-user accounts, as Member or Contributor.
For the service principal relying on the DAX discovery fallback, Viewer is not enough — a Viewer cannot run executeQueries. Use Member or Contributor, or enable the read-only admin APIs above. Row-level security is the exception: a per-user identity that must be RLS-filtered should be Viewer or lower (Member/Contributor bypass RLS) — see the RLS step below.
2
Grant Build permission on the semantic models
Build is the permission that actually lets an identity run DAX (executeQueries) — Read alone is not enough. Workspace Member/Contributor typically confers Build; you can also grant it directly on the model (the model’s Manage permissions → Add user → Build). Direct Build with no workspace role is exactly what lets a per-user identity query a model under row-level security (see the next step).
3
(Row-level security) Use per-user auth and map each user into an RLS role
Row-level security is enforced against the querying identity, and two Microsoft rules decide the setup:
A service principal cannot query an RLS model at all. It cannot be added to an RLS role, so executeQueries returns 401 — even if the service principal is a workspace Member. Use Sign in with Microsoft (per-user) for any RLS-protected model; the service principal can still index non-RLS models in the same tenant.
An edit-level workspace role bypasses RLS. Admin, Member, and Contributor have edit permission, so RLS does not apply to them. For RLS to take effect a user must hold at most Viewer, or no workspace role with the model shared to them directly.
So the correct setup for an RLS model is, per user: Build permission on the model and membership in the appropriate RLS role, with no edit-level workspace role. Bag of words discovers and queries the model with that user’s own token, and Power BI filters the rows to their role.
Go to Settings → Data Sources → Add data source → Power BI and fill in the form. See Connecting a data source for the general flow.
Field
Required
Default
Notes
tenant_id
Yes
—
Azure AD Tenant ID (Directory ID) from the app’s Overview page.
client_id
Yes
—
Azure AD App Registration Client ID.
client_secret
Yes
—
Azure AD App Registration secret value you copied in Step 1.
oauth_client_id
No
—
App Registration Client ID used for user sign-in (authorization code flow). If blank, falls back to client_id.
oauth_client_secret
No
—
Secret for the user-sign-in app. If blank, falls back to client_secret.
workspaces
No
—
Workspace name(s) or ID(s), comma-separated, to limit discovery. If empty, all accessible workspaces are discovered.
1
Choose the authentication
Pick Service Principal (Azure AD) for a shared identity, or Sign in with Microsoft for per-user (delegated) access. The Sign in with Microsoft mode has no fields of its own — it reuses the app registration configured above.
2
Test the connection
Click Test Connection. Bag of words authenticates, lists workspaces, and probes a semantic model to verify query access. A model that is empty or not queryable does not fail the test — it reports that query access was verified.
3
Require user authentication (optional)
To force every user to sign in with their own Microsoft account instead of using the shared service principal, enable Require user authentication under the data source’s access settings. Each user then clicks Connect to sign in before they can query. See Authentication.
4
Select tables and save
After the connection indexes, choose which tables the AI agent may use, then save. See Data sources overview for managing tables, instructions, and access.
Once connected, the AI agent writes and runs DAX against your semantic models automatically. Tables are addressed as Dataset/Table, and relationships defined in the model are available for joins. You do not need to configure anything further to start asking questions.
With per-user authentication, a dashboard built on a Power BI model can be shared so that each viewer’s queries run under their own token. Power BI’s row-level security then filters the rows per person — one shared dashboard, and every viewer sees only what their RLS role allows. Configure this with Viewer run identity when sharing; see personalizing a dashboard.
View-as on a Power BI (delegated) source previews the target identity’s parameter binding but keeps your token, so it shows your source-level rows, not theirs. To confirm the exact rows another person gets under RLS, have them open the shared dashboard themselves.
Troubleshooting: a model is missing from the schema
If a semantic model is visible in app.powerbi.com but not selectable in Bag of words, work through these in order:
The identity is not a Member/Contributor of the workspace
For the service principal, discovery only sees workspaces where it has a role — add it as Member or Contributor, and note that content in My Workspace is not discovered. With per-user authentication this is looser: a model shared directly with the signed-in user (Build permission) is discovered even with no workspace role, so a direct dataset share is sufficient there — which is the setup RLS requires.
The model cannot be introspected (DirectLake / no Build / RLS)
Without the read-only admin APIs, schema comes from a DAX probe that needs Build permission and does not work for some DirectLake models. Enable Service principals can access read-only admin APIs + Enhanced admin API responses with detailed metadata in the tenant settings, and grant Build on the model. The connection’s indexing report lists models that were found but could not be read, with the reason.
The model was not refreshed after enabling enhanced metadata
The Scanner API returns no schema for models not refreshed since detailed-metadata scanning was enabled. Refresh the model once and re-index the connection.
A per-user model isn't showing for a signed-in user
With per-user authentication, the user must have completed Connect (Microsoft sign-in). Once signed in, models their own account can access become selectable even if the shared service principal cannot see them. If access was just changed in Power BI, note that Power BI caches user permissions — Bag of words refreshes them on sign-in and on Reload tables, so click Reload if a newly granted model hasn’t appeared yet.
A per-user model is discovered but the agent can't query it yet
A model that only a signed-in user can see (for example an RLS model the service principal cannot index) is added to the catalog but starts unselected. An admin or the data source owner must enable it on the Select tables step before the agent can query it. Until then it appears in the user’s table list but not in the agent’s context.
The workspaces filter is excluding it
If you set workspaces, only those workspaces are discovered. Clear it to discover everything, or add the workspace’s name/ID.