# How to Integrate Bolt.new with Microsoft Power BI

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: April 2026

## TL;DR

Integrate Bolt.new with Microsoft Power BI Embedded by registering an Azure AD app, generating embed tokens through a Next.js API route, and embedding reports using the powerbi-client JavaScript library. Requires an Azure AD tenant and Power BI Pro or Embedded capacity license. For lightweight analytics without licensing complexity, Google Looker Studio (formerly Data Studio) is a free alternative.

## Embedding Power BI Reports in Bolt.new Apps

Power BI Embedded lets you embed interactive Power BI reports, dashboards, and Q&A directly in your own web application. For organizations that already have Power BI reports built by analysts, embedding them in a Bolt.new app can surface those insights in a product context — inside a customer portal, an internal operations tool, or an executive dashboard — without requiring every user to have a Power BI account or access the Power BI service directly.

The embedding architecture has three components. First, an Azure Active Directory app registration provides the application identity and grants permission to call the Power BI REST API. Second, your server-side API route uses these Azure AD credentials to get an access token and call the Power BI REST API to generate an embed token — a short-lived credential specific to one report. Third, the powerbi-client JavaScript library in your React component uses the embed token to authenticate with Power BI's embedding service and render the interactive report iframe.

Licensing is the most important thing to understand before starting. There are two supported paths: Embed for your organization (users need their own Power BI Pro or Premium Per User licenses — appropriate for internal apps), and Embed for your customers (your app has a Power BI Embedded capacity SKU — appropriate for customer-facing products). If you are prototyping, either path requires at minimum a Power BI Pro trial account. For teams evaluating alternatives, Google Looker Studio is free and embeddable, though less capable than Power BI for complex enterprise reporting needs.

## Before you start

- A Microsoft Azure account with permission to register Azure AD applications (Azure portal)
- A Power BI Pro license or Power BI Embedded capacity (A1 SKU minimum for testing) — free Power BI accounts cannot generate embed tokens
- Power BI report(s) published to a Power BI workspace (not My Workspace — must be a collaborative workspace)
- The workspace ID and report ID from the Power BI service URL (visible when viewing the report in powerbi.com)
- A Next.js project in Bolt.new with the @azure/msal-node and powerbi-client-react packages installed

## Step-by-step guide

### 1. Register an Azure AD app and configure Power BI permissions

Power BI's embedding API requires an Azure AD application to authenticate API calls. Go to portal.azure.com, navigate to Azure Active Directory → App registrations → New registration. Give the app a name (e.g., 'Bolt Power BI Embed'), set the account type to 'Accounts in this organizational directory only' for single-tenant use, and click Register. Note the Application (client) ID and Directory (tenant) ID shown on the overview page.

For authentication, choose between two approaches. Service principal (recommended for production): go to Certificates & secrets → New client secret, create a secret with a 24-month expiry, and copy the value immediately — it is only shown once. User principal: you use a dedicated Power BI admin account's credentials. Service principal is more secure and is the approach covered here.

Next, grant API permissions. In your app registration, click API permissions → Add a permission → Power BI Service. For the 'Embed for customers' scenario using service principal, you need Delegated permissions: Report.ReadAll and Dataset.ReadAll. Click 'Grant admin consent' after adding the permissions — without this, the consent is pending and API calls will fail.

Finally, enable service principal access in the Power BI admin portal. Go to app.powerbi.com → Admin portal → Tenant settings → Developer settings → 'Allow service principals to use Power BI APIs' → Enable. Also enable 'Allow service principals to create and use profiles'. Then, in your Power BI workspace, add the service principal as a Member with Contributor role: Workspace settings → Access → add the Azure AD app by name.

```
// .env.local
POWERBI_TENANT_ID=your_azure_ad_tenant_id
POWERBI_CLIENT_ID=your_azure_ad_app_client_id
POWERBI_CLIENT_SECRET=your_azure_ad_client_secret
POWERBI_WORKSPACE_ID=your_power_bi_workspace_id
POWERBI_REPORT_ID=your_power_bi_report_id
```

**Expected result:** Your Azure AD app is registered with Power BI API permissions granted, the service principal is added to the Power BI workspace as a Contributor, and the four credential values are in your .env.local file.

### 2. Build the token generation API route

Generating a Power BI embed token requires two API calls: one to Azure AD for an access token, and one to the Power BI REST API to exchange that access token for a report-specific embed token. The embed token is scoped to a specific report (and optionally specific RLS identities) and expires after one hour.

Install the required packages. Run npm install @azure/msal-node in Bolt's terminal. The MSAL Node library handles Azure AD authentication correctly and is the recommended approach over manual HTTP calls.

The Power BI embed token API endpoint is POST https://api.powerbi.com/v1.0/myorg/groups/{workspaceId}/reports/{reportId}/GenerateToken. The request body specifies the access level ('view' for read-only) and optional RLS identities if your report uses row-level security. The response includes an embed token string and an expiration time.

The embed token, combined with the report's embed URL (fetched from the Power BI REST API), is what the client-side powerbi-client library needs to render the report. Fetch both in the same API route and return them together.

```
// lib/powerbi.ts
import { ConfidentialClientApplication } from '@azure/msal-node';

let msalApp: ConfidentialClientApplication | null = null;

function getMsalApp() {
  if (!msalApp) {
    msalApp = new ConfidentialClientApplication({
      auth: {
        clientId: process.env.POWERBI_CLIENT_ID!,
        clientSecret: process.env.POWERBI_CLIENT_SECRET!,
        authority: `https://login.microsoftonline.com/${process.env.POWERBI_TENANT_ID}`,
      },
    });
  }
  return msalApp;
}

export async function getPowerBIAccessToken(): Promise<string> {
  const app = getMsalApp();
  const result = await app.acquireTokenByClientCredential({
    scopes: ['https://analysis.windows.net/powerbi/api/.default'],
  });
  if (!result?.accessToken) throw new Error('Failed to acquire Power BI access token');
  return result.accessToken;
}

// app/api/powerbi/embed-token/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { getPowerBIAccessToken } from '@/lib/powerbi';

const PBI_API = 'https://api.powerbi.com/v1.0/myorg';

export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const workspaceId = searchParams.get('workspaceId') ?? process.env.POWERBI_WORKSPACE_ID!;
  const reportId = searchParams.get('reportId') ?? process.env.POWERBI_REPORT_ID!;

  const accessToken = await getPowerBIAccessToken();
  const headers = { Authorization: `Bearer ${accessToken}` };

  // Step 1: Get the report's embed URL
  const reportResponse = await fetch(
    `${PBI_API}/groups/${workspaceId}/reports/${reportId}`,
    { headers }
  );
  if (!reportResponse.ok) {
    return NextResponse.json({ error: 'Failed to fetch report metadata' }, { status: reportResponse.status });
  }
  const report = await reportResponse.json();

  // Step 2: Generate the embed token
  const tokenResponse = await fetch(
    `${PBI_API}/groups/${workspaceId}/reports/${reportId}/GenerateToken`,
    {
      method: 'POST',
      headers: { ...headers, 'Content-Type': 'application/json' },
      body: JSON.stringify({ accessLevel: 'view' }),
    }
  );
  if (!tokenResponse.ok) {
    return NextResponse.json({ error: 'Failed to generate embed token' }, { status: tokenResponse.status });
  }
  const tokenData = await tokenResponse.json();

  return NextResponse.json({
    reportId,
    embedUrl: report.embedUrl,
    embedToken: tokenData.token,
    tokenExpiry: tokenData.expiration,
  });
}
```

**Expected result:** Calling /api/powerbi/embed-token returns a JSON object with embedUrl, embedToken, and tokenExpiry that the client-side component needs to render the Power BI report.

### 3. Embed the Power BI report in a React component

With an embed token and embed URL from the API route, you can render the Power BI report in your React component using the powerbi-client-react library. Install it: npm install powerbi-client-react powerbi-client. The library provides a PowerBIEmbed React component that wraps the Power BI JavaScript SDK.

The PowerBIEmbed component takes an embedConfig object with the report type, embed URL, access token, and display settings. It also accepts event handlers for events like rendered (when the report finishes loading), error, and dataSelected (when a user clicks a data point). These events let you respond to user interactions within the embedded report from your React app.

Token expiry handling is important for production. Embed tokens expire after one hour. The Power BI client library fires a tokenExpired event when the current token is about to expire. Handle this event by calling your API route for a new embed token and passing it to the component's updateSettings method. If you do not handle token expiry, the embedded report becomes non-interactive after an hour without refreshing the page.

For Row Level Security (RLS), add an identities array to the GenerateToken request body: identities: [{ username: user.email, roles: ['SalesRegion_East'], datasets: [dataset_id] }]. The RLS role and dataset ID must match the RLS configuration in your Power BI report.

```
// components/PowerBIReport.tsx
'use client';
import { useEffect, useState, useCallback } from 'react';
import { PowerBIEmbed } from 'powerbi-client-react';
import { models, Report } from 'powerbi-client';

interface EmbedConfig {
  embedUrl: string;
  embedToken: string;
  reportId: string;
  tokenExpiry: string;
}

interface PowerBIReportProps {
  reportId?: string;
  workspaceId?: string;
  height?: number;
}

export function PowerBIReport({ reportId, workspaceId, height = 600 }: PowerBIReportProps) {
  const [config, setConfig] = useState<EmbedConfig | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const fetchEmbedToken = useCallback(async () => {
    try {
      const params = new URLSearchParams();
      if (reportId) params.set('reportId', reportId);
      if (workspaceId) params.set('workspaceId', workspaceId);

      const response = await fetch(`/api/powerbi/embed-token?${params.toString()}`);
      if (!response.ok) throw new Error('Failed to fetch embed token');

      const data = await response.json();
      setConfig(data);
      setError(null);
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Failed to load report');
    } finally {
      setLoading(false);
    }
  }, [reportId, workspaceId]);

  useEffect(() => { fetchEmbedToken(); }, [fetchEmbedToken]);

  if (loading) return <div style={{ height }} className="animate-pulse bg-gray-100 rounded" />;
  if (error) return (
    <div className="flex flex-col items-center justify-center" style={{ height }}>
      <p className="text-red-500">{error}</p>
      <button onClick={fetchEmbedToken} className="mt-2 px-4 py-2 bg-blue-500 text-white rounded">
        Retry
      </button>
    </div>
  );
  if (!config) return null;

  return (
    <PowerBIEmbed
      embedConfig={{
        type: 'report',
        id: config.reportId,
        embedUrl: config.embedUrl,
        accessToken: config.embedToken,
        tokenType: models.TokenType.Embed,
        settings: {
          navContentPaneEnabled: false,
          filterPaneEnabled: false,
        },
      }}
      cssClassName="powerbi-report-container"
      getEmbeddedComponent={(embeddedReport) => {
        (embeddedReport as Report).on('tokenExpired', () => {
          fetchEmbedToken();
        });
      }}
      style={{ height: `${height}px`, width: '100%' }}
    />
  );
}
```

**Expected result:** The PowerBIReport component renders a fully interactive Power BI report in the browser. Users can use report filters, drill through data, and export visuals using Power BI's embedded toolbar.

## Best practices

- Use service principal authentication for production deployments — it does not depend on a user account password that can expire or be changed, making it more reliable than user-based credentials.
- Store all Azure AD credentials (POWERBI_CLIENT_ID, POWERBI_CLIENT_SECRET, POWERBI_TENANT_ID) in server-side environment variables and never in NEXT_PUBLIC_ prefixed variables or client-side code.
- Handle the tokenExpired event from the powerbi-client library and refresh the embed token automatically — users should not need to reload the page when the one-hour token expires during a work session.
- For simpler analytics needs without Azure AD complexity, evaluate Google Looker Studio (free) or Tableau Public (free for public data) as alternatives before committing to the Power BI Embedded licensing and setup requirements.
- Use Row Level Security in your Power BI reports and pass user identity in the GenerateToken request to automatically scope data visibility — this is more secure than maintaining separate reports per user.
- Disable the filter pane and navigation pane in the embedConfig settings for most embedded scenarios — they add visual complexity that is usually not appropriate when the report is embedded in a larger application UI.
- Embed tokens expire after one hour but can be refreshed without user interaction. Implement a background refresh that fetches a new token 5 minutes before expiry and passes it to the embedded report using the report.setAccessToken() method.

## Use cases

### Embedded operations dashboard

An operations team has built a comprehensive Power BI report showing supply chain metrics, inventory levels, and order fulfillment rates. Embed that report in your internal ops portal so the team can access it without logging into Power BI separately, with single sign-on from your app's authentication.

Prompt example:

```
Add a Power BI embedded analytics section to my internal operations dashboard. Create a component that calls /api/powerbi/embed-token with a workspace ID and report ID, then renders the Power BI report using the powerbi-client-react library. The report should fill the full width of the content area at 650px height with the toolbar hidden.
```

### Per-customer analytics in a SaaS product

Your SaaS product generates usage data stored in a data warehouse connected to Power BI. Each customer has a Power BI report scoped to their data using Row Level Security (RLS). Embed the report in your customer portal with the customer's identity passed through RLS so each customer only sees their own data.

Prompt example:

```
Create a customer analytics page that embeds a Power BI report with Row Level Security. When generating the embed token, include the user's company ID as the RLS username so Power BI filters the data to show only that company's records. Use the 'Embed for customers' pattern with a service principal.
```

### Executive KPI summary

Executives want a single-page view of the company's key performance indicators pulled from multiple Power BI reports. Embed specific visualizations from different reports as tiles in a custom layout, creating a curated view that does not exist as a single report in Power BI.

Prompt example:

```
Build an executive dashboard that embeds multiple Power BI report pages. Create a tab navigation component where each tab shows a different page of the main executive report, embedded using the Power BI client library with the page parameter set. Disable the navigation pane inside the embedded report.
```

## Troubleshooting

### API route returns 403 when calling GenerateToken — 'The caller does not have permission to perform this operation'

Cause: The service principal is not added to the Power BI workspace with at least Contributor role, or the Power BI tenant admin has not enabled 'Allow service principals to use Power BI APIs' in the admin portal.

Solution: In the Power BI Admin portal (app.powerbi.com → Admin portal → Tenant settings), verify 'Allow service principals to use Power BI APIs' is enabled for all users or your specific security group. Then in your Power BI workspace settings → Access, confirm the Azure AD app is listed with Member or Contributor role. Changes to tenant settings can take up to 15 minutes to propagate.

### MSAL throws 'AADSTS70011: The provided request must include a resource input parameter'

Cause: The scopes parameter in the token request is incorrect. Power BI requires the scope to be exactly 'https://analysis.windows.net/powerbi/api/.default' including the /.default suffix.

Solution: Verify the scopes array in the acquireTokenByClientCredential call is exactly ['https://analysis.windows.net/powerbi/api/.default']. The /.default suffix is required for client credentials flow — it tells Azure AD to use all statically configured application permissions.

```
// Correct scope format for Power BI client credentials:
scopes: ['https://analysis.windows.net/powerbi/api/.default']
// NOT: 'https://analysis.windows.net/powerbi/api'
```

### The Power BI embed component shows 'Sorry, we ran into an issue' error in the embedded iframe

Cause: The embed token is valid but the report embed URL is incorrect, the report has been moved or deleted, or the report requires a capacity that is not available. This can also happen if the embed token was generated for a different report ID than the one in the embedConfig.

Solution: Verify that reportId in the API response matches what is set in the PowerBIEmbed embedConfig. Check that the report still exists in the Power BI workspace by visiting it directly at app.powerbi.com. Ensure the workspace has an active Premium capacity or Power BI Embedded capacity if using the 'embed for customers' scenario.

## Frequently asked questions

### Can I test the Power BI embedding in Bolt's development preview?

Yes. The Power BI embed token API calls are outbound HTTPS requests from your server-side Next.js routes, which work fine in Bolt's WebContainer. The powerbi-client library is a browser-side JavaScript library that also works in the preview. The main constraint is that both your Azure AD app permissions and Power BI workspace access must be correctly configured before any of the embedding works — these are setup steps that happen outside of Bolt.

### Does Bolt.new have a native Power BI integration?

No. Power BI is not one of Bolt's native connectors. You build the integration yourself using Next.js API routes and the Power BI REST API. Bolt's AI can generate the integration code when you describe what analytics embedding features you need, but Azure AD app registration and Power BI workspace configuration must be done manually in the Azure and Power BI portals.

### What Power BI license do I need to embed reports?

For internal apps where all viewers are in your organization (Embed for your organization), each viewer needs a Power BI Pro license ($10/user/month) or a Power BI Premium Per User license ($20/user/month). For customer-facing apps where viewers are outside your organization (Embed for your customers), your organization needs a Power BI Embedded capacity SKU (starting at A1 SKU, approximately $735/month). Free Power BI accounts cannot generate embed tokens.

### Can I embed Power BI dashboards as well as reports?

Yes. The Power BI Embedded API supports embedding reports, dashboards, tiles (individual visuals from a dashboard), Q&A experiences, and paginated reports. The PowerBIEmbed component's type field accepts 'report', 'dashboard', 'tile', 'qna', or 'paginated-report'. Each type has slightly different API endpoints for generating the embed token and embed URL.

### How do I pass user-specific filters to an embedded Power BI report?

There are two approaches. For Row Level Security (recommended for data security), configure RLS roles in Power BI Desktop and pass the user's identity in the GenerateToken API call's identities array. Power BI automatically applies the RLS filter server-side. For display-level filters (not enforced at data level), use the powerbi-client API's setFilters() method after the report renders to apply visual filters based on the current user's context.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/microsoft-power-bi
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/microsoft-power-bi
