# How to Integrate Bolt.new with Mural

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

## TL;DR

Integrate Mural's REST API into Bolt.new to create, read, and embed collaborative whiteboards programmatically. The quickest path is embedding murals via iframe — Mural provides embed URLs for each whiteboard that work in Bolt's WebContainer preview instantly. Full REST API access (creating murals, adding sticky notes) requires a Mural Business or Enterprise plan and server-side API routes to protect your token.

## Embed and Manage Mural Whiteboards from Bolt.new

Mural is the go-to platform for structured facilitation in distributed teams — design sprints, workshops, retrospectives, and strategic planning sessions all benefit from Mural's template library and real-time collaboration features. Embedding Mural directly into your project management app, intranet portal, or client dashboard eliminates the context switch of opening a separate Mural window and gives your team a unified workspace.

There are two distinct integration patterns with different requirements. The first and simplest is the embed approach: Mural generates a sharable embed URL for any whiteboard (mural board) in your workspace. You paste this URL into an iframe, and the full interactive Mural experience renders inside your app. This works with any Mural plan, requires no API credentials, and functions in Bolt.new's WebContainer preview instantly — ideal for dashboards and portals where you want to surface specific, known murals.

The second pattern is the REST API: programmatic creation and management of murals, adding stickies and content, reading workshop outputs, and dynamically loading murals for different projects. The Mural REST API requires a Business or Enterprise subscription — the Free and Team plans do not include API access. If your organization has a Business or Enterprise account, the API enables powerful automation: auto-creating a retrospective mural when a new sprint begins, reading sticky note content for meeting summaries, or syncing mural participants with your project roster.

## Before you start

- A Mural account — the free plan works for embed-only integrations; Business or Enterprise plan required for REST API access
- A Mural API token from the Mural Developer Portal (app.mural.co/api) — only available on Business/Enterprise plans
- The workspace ID and room ID from your Mural workspace URL for API calls that require specifying a location
- A Bolt.new account with a new Next.js project open
- For full API use: at least one existing mural in your workspace to test read operations

## Step-by-step guide

### 1. Get your Mural embed URL and API credentials

Mural integration starts with understanding which access level you have. If you have a Free or Team Mural account, you can use the embed approach immediately — no API credentials needed. If you have a Business or Enterprise account, you can additionally use the REST API for programmatic mural management.

For the embed URL (works on all plans): open any mural in Mural, click the Share button in the top-right corner, select 'Get embed link', choose 'Visitor access' (view and collaborate without requiring a Mural account) or 'Member access', and copy the embed URL. It looks like `https://app.mural.co/embed/...`. This URL can be placed directly in an iframe in your Bolt.new app — no server-side code required.

For the REST API (Business/Enterprise only): go to app.mural.co/api or navigate to your Mural workspace settings → Developers → API Access. Create a new API application and generate a Personal Access Token (PAT). The token is a long string starting with `eyJ...` (a JWT). Copy it immediately — it may not be shown again. Add it to your .env file as MURAL_API_TOKEN.

You will also need your workspace ID, which appears in the Mural URL as `https://app.mural.co/t/{workspaceId}/...`. For API calls that create murals in a specific room, also note the room ID from `https://app.mural.co/t/{workspaceId}/r/{roomId}`.

All API calls to Mural use the base URL `https://app.mural.co/api/public/v1`. The token goes in the Authorization header as `Bearer {token}`. Outbound calls to this URL work from Bolt's WebContainer during development.

```
// lib/mural.ts
const MURAL_API_BASE = 'https://app.mural.co/api/public/v1';

export async function muralFetch<T = unknown>(
  endpoint: string,
  init: RequestInit = {}
): Promise<T> {
  const token = process.env.MURAL_API_TOKEN;
  if (!token) {
    throw new Error(
      'MURAL_API_TOKEN is not set. Get it from app.mural.co → Settings → Developers. ' +
      'Note: REST API access requires a Mural Business or Enterprise plan.'
    );
  }

  const url = `${MURAL_API_BASE}${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
  const response = await fetch(url, {
    ...init,
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
      Accept: 'application/json',
      ...init.headers,
    },
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Mural API ${response.status} ${endpoint}: ${errorText}`);
  }

  return response.json() as Promise<T>;
}

// Generate a Mural embed URL from a mural share link or ID
export function getMuralEmbedUrl(muralShareLink: string): string {
  // If it's already an embed URL, return as-is
  if (muralShareLink.includes('/embed/')) return muralShareLink;
  // Convert share link to embed URL format
  return muralShareLink.replace('/t/', '/embed/t/');
}
```

**Expected result:** A configured Mural API helper in lib/mural.ts with credentials in .env, and the ability to embed any mural via iframe using the embed URL approach.

### 2. Embed a Mural whiteboard using iframe

The Mural embed is the fastest path to showing a collaborative whiteboard inside your Bolt.new app. It requires no API credentials, works on all Mural plans, and functions immediately in Bolt's WebContainer preview. The iframe renders the full Mural experience — sticky notes, shapes, cursor tracking, and real-time collaboration — as if users were on app.mural.co directly.

Mural's embed URLs are generated per-mural and include access control: you can set them to require Mural account login or allow anonymous visitor access. For internal tools, 'Member access' ensures only your workspace members can interact. For client portals where external stakeholders participate, 'Visitor access' lets anyone with the link join without needing a Mural account.

The iframe embed has a few important configuration details. The sandbox attribute controls what the iframe is allowed to do — Mural requires at minimum `allow-same-origin allow-scripts allow-popups allow-forms` for full functionality. Without `allow-popups`, some Mural features (file upload, external links) may not work. Without `allow-same-origin`, Mural cannot store settings in the browser.

For responsive embeds, set the iframe to `width: 100%` and control height explicitly — Mural does not auto-resize to fit its content. A height of 600-800px works well for most workshop use cases. On mobile, Mural embeds can be difficult to use — consider showing a 'Open in Mural' link on small screens instead of the embedded iframe.

When building a project-specific Mural dashboard, store the embed URL in your database alongside other project data (Supabase, etc.) so each project can have its own associated mural without hardcoding URLs.

```
// components/MuralEmbed.tsx
'use client';
import { useState } from 'react';

interface MuralEmbedProps {
  embedUrl: string;
  title?: string;
  height?: number;
  showOpenLink?: boolean;
}

export function MuralEmbed({
  embedUrl,
  title = 'Mural Whiteboard',
  height = 600,
  showOpenLink = true,
}: MuralEmbedProps) {
  const [isLoaded, setIsLoaded] = useState(false);

  // Convert share URL to embed URL if needed
  const iframeUrl = embedUrl.includes('/embed/')
    ? embedUrl
    : embedUrl.replace('/t/', '/embed/t/');

  return (
    <div className="w-full">
      {/* Desktop: show embed */}
      <div className="hidden md:block relative">
        {!isLoaded && (
          <div
            className="absolute inset-0 bg-gray-100 animate-pulse rounded-lg flex items-center justify-center"
            style={{ height }}
          >
            <span className="text-gray-500 text-sm">Loading Mural...</span>
          </div>
        )}
        <iframe
          src={iframeUrl}
          title={title}
          width="100%"
          height={height}
          sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-pointer-lock"
          allowFullScreen
          onLoad={() => setIsLoaded(true)}
          className="rounded-lg border border-gray-200"
        />
      </div>

      {/* Mobile: show link only */}
      <div className="md:hidden bg-gray-50 border border-gray-200 rounded-lg p-6 text-center">
        <p className="text-gray-600 mb-3">Open this mural on a larger screen for the best experience</p>
        {showOpenLink && (
          <a
            href={embedUrl}
            target="_blank"
            rel="noopener noreferrer"
            className="inline-flex items-center gap-2 text-blue-600 hover:underline font-medium"
          >
            Open in Mural
          </a>
        )}
      </div>

      {showOpenLink && (
        <div className="hidden md:flex justify-end mt-2">
          <a
            href={embedUrl}
            target="_blank"
            rel="noopener noreferrer"
            className="text-sm text-gray-500 hover:text-gray-700 hover:underline"
          >
            Open in Mural ↗
          </a>
        </div>
      )}
    </div>
  );
}
```

**Expected result:** A reusable MuralEmbed component that displays any Mural whiteboard in an iframe with loading state, responsive behavior, and an Open in Mural link.

### 3. List and fetch murals via the REST API

With the Mural REST API (Business/Enterprise plans), you can programmatically list all murals in your workspace, filter them by room or workspace, and display them dynamically in your app without hardcoding embed URLs. This powers scenarios like a project portal that automatically shows the mural associated with each project.

The primary listing endpoint is `GET /workspaces/{workspaceId}/murals`. This returns a paginated list of murals in your workspace with fields including `id`, `title`, `createdOn`, `updatedOn`, `shareLink`, `status`, and the room/workspace they belong to. Use `next` (cursor-based pagination) to fetch all pages for large workspaces.

For room-specific murals: `GET /rooms/{roomId}/murals` returns only murals in that room. If your workspace is organized into rooms by client, project, or team, this lets you build workspace-aware navigation — showing the team lead only murals from their team's room.

The `shareLink` in the API response is the full Mural URL. Converting it to an embed URL is straightforward: replace `/t/` with `/embed/t/` in the path. Store the shareLink in your database when creating murals programmatically, and generate embed URLs dynamically in your frontend.

For searching and filtering murals, the list API supports `title` (fuzzy match), `status` (active, archived), and date range filters. This is useful for a mural library feature where team members can search for past workshop outputs by topic or date.

```
// app/api/mural/murals/route.ts
import { NextResponse } from 'next/server';
import { muralFetch, getMuralEmbedUrl } from '@/lib/mural';

interface MuralItem {
  id: string;
  title: string;
  createdOn: number;
  updatedOn: number;
  shareLink: string;
  status: string;
}

interface MuralListResponse {
  value: MuralItem[];
  next?: string;
}

export async function GET() {
  const workspaceId = process.env.MURAL_WORKSPACE_ID;
  if (!workspaceId) {
    return NextResponse.json(
      { error: 'MURAL_WORKSPACE_ID is not configured' },
      { status: 500 }
    );
  }

  const data = await muralFetch<MuralListResponse>(
    `/workspaces/${workspaceId}/murals?limit=50`
  );

  const murals = (data.value ?? []).map(m => ({
    id: m.id,
    title: m.title,
    createdOn: new Date(m.createdOn).toISOString(),
    updatedOn: new Date(m.updatedOn).toISOString(),
    shareLink: m.shareLink,
    embedUrl: getMuralEmbedUrl(m.shareLink),
    status: m.status,
  }));

  return NextResponse.json({ murals, total: murals.length });
}
```

**Expected result:** A murals API route that returns all workspace murals with embed URLs, and a library component displaying them in a searchable grid.

### 4. Create murals programmatically and deploy the integration

The Mural creation API (`POST /rooms/{roomId}/murals`) lets you generate new whiteboards on demand — useful for automating the setup of recurring workshops, creating dedicated murals for each new project or client, or building a self-service portal where team members can request a new whiteboard.

The create request body requires `title` (the mural name) and optionally `roomId`, `workspaceId`, `templateId`, `backgroundColor`, `height`, and `width`. Templates are particularly valuable for standardized workshops — a retrospective template ensures every sprint retro starts with the same layout and sections. Template IDs can be found in your Mural workspace's template library.

The created mural response includes the full mural object with the `shareLink` and `id`. Save these to your database immediately — you will need the ID if you want to add content later, and the shareLink for embedding. Store both `shareLink` and `id` so you have both the embed URL and the ability to make subsequent API calls on the specific mural.

After creating a mural, Mural takes a few seconds to fully initialize the new whiteboard. If you embed the mural immediately after creation, it may show a loading screen briefly. Consider adding a polling check or a 3-second delay before redirecting users to the new mural.

For deployment: all Mural API calls are outbound HTTP requests that work in Bolt's WebContainer during development. There is no webhook or incoming connection requirement for the create, list, or embed use cases. Deploy to Netlify when you are ready for production, and add your MURAL_API_TOKEN, MURAL_WORKSPACE_ID, and MURAL_DEFAULT_ROOM_ID environment variables in Netlify's dashboard.

```
// app/api/mural/create/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { muralFetch, getMuralEmbedUrl } from '@/lib/mural';

interface CreatedMural {
  id: string;
  title: string;
  shareLink: string;
  createdOn: number;
}

export async function POST(request: NextRequest) {
  const { title, templateId, roomId } = await request.json();

  if (!title?.trim()) {
    return NextResponse.json({ error: 'title is required' }, { status: 400 });
  }

  const targetRoomId = roomId ?? process.env.MURAL_DEFAULT_ROOM_ID;
  if (!targetRoomId) {
    return NextResponse.json(
      { error: 'roomId is required — set MURAL_DEFAULT_ROOM_ID in .env or pass roomId in request' },
      { status: 400 }
    );
  }

  const body: Record<string, unknown> = { title: title.trim() };
  if (templateId) body.templateId = templateId;

  const mural = await muralFetch<CreatedMural>(`/rooms/${targetRoomId}/murals`, {
    method: 'POST',
    body: JSON.stringify(body),
  });

  return NextResponse.json({
    id: mural.id,
    title: mural.title,
    shareLink: mural.shareLink,
    embedUrl: getMuralEmbedUrl(mural.shareLink),
    createdOn: new Date(mural.createdOn).toISOString(),
  }, { status: 201 });
}
```

**Expected result:** A mural creation API route that generates new Mural whiteboards programmatically, with the created mural's embed URL ready for immediate use in your app.

## Best practices

- Store MURAL_API_TOKEN as a server-side environment variable only — never use NEXT_PUBLIC_ prefix, as the token provides write access to your entire workspace
- Use the iframe embed approach for most dashboard use cases — it requires no API credentials, works on all Mural plans, and delivers the full interactive Mural experience
- Store both the mural shareLink and ID in your database when creating murals programmatically — the shareLink is needed for embedding and the ID is needed for subsequent API calls
- Set iframe embed permissions correctly: allow-same-origin allow-scripts allow-popups allow-forms allow-pointer-lock are all needed for Mural to function fully inside an iframe
- For mobile users, replace the iframe embed with a direct link to the Mural app — Mural embeds are difficult to use on small screens due to the nature of whiteboarding interactions
- Use Mural templates for standardized workshops to ensure consistency across retrospectives, design sprints, and planning sessions
- Implement cursor-based pagination for workspace-wide mural listings — large organizations may have hundreds of murals and fetching all at once can cause slow API responses
- Scope your Mural API token permissions to the minimum required — use read-only tokens for dashboard applications that only need to list and embed murals, reserving write-capable tokens for automation workflows

## Use cases

### Project Kickoff Dashboard with Embedded Mural

Build a project management portal where each project has a dedicated page embedding the corresponding Mural whiteboard. Team members can view and collaborate on the mural directly within your app, with the project's tasks and timeline displayed alongside it.

Prompt example:

```
Create a project detail page that embeds a Mural whiteboard. Build a React component at components/MuralEmbed.tsx that accepts a muralEmbedUrl string and an optional title. Render it in a responsive iframe container with a loading state. The iframe should have sandbox='allow-same-origin allow-scripts allow-popups allow-forms' permissions. Show a 'Open in Mural' link below the iframe that opens the full Mural board in a new tab. Add a configurable height prop defaulting to 600px.
```

### Workshop Library Dashboard

Create an internal dashboard listing all murals in your workspace with their creation date, template used, and a quick-launch button. Team members can browse available workshop templates and open or embed any mural without navigating the Mural interface.

Prompt example:

```
Build a Mural library dashboard. Create an API route at app/api/mural/murals/route.ts that fetches all murals from the Mural REST API using a Bearer token from MURAL_API_TOKEN in process.env. Call GET https://app.mural.co/api/public/v1/murals and return id, title, createdOn, and embedUrl for each mural. Build a React MuralLibrary component showing murals in a card grid with title, creation date, and an Embed button that shows the selected mural in an iframe modal.
```

### Auto-Create Sprint Retrospective Murals

When a new sprint or project phase begins, automatically create a fresh Mural whiteboard based on your retrospective template. The created mural URL is saved to your database and embedded in the sprint page, ready for the team without any manual setup.

Prompt example:

```
Create a function that auto-creates a Mural whiteboard for new sprints. Build an API route at app/api/mural/create/route.ts that accepts POST with title and optional templateId. Use the MURAL_API_TOKEN to POST to the Mural REST API to create a new mural in the workspace (room) specified by MURAL_ROOM_ID. Return the created mural's id, title, and shareLink. Use this in my sprint creation workflow to automatically provision a retro board for each sprint.
```

## Troubleshooting

### Mural iframe embed shows a blank screen or login prompt instead of the whiteboard

Cause: Two common causes: (1) the embed URL was generated with 'Private' access rather than 'Visitor' or 'Member' access — private murals require a logged-in Mural account and cannot be embedded without authentication, or (2) the iframe sandbox attribute is missing allow-same-origin, which prevents Mural's session cookies from being read.

Solution: Regenerate the embed URL in Mural with Visitor Access (for public embeds) or Member Access (for internal tools). Go to the mural → Share → Get embed link → select access level → copy URL. Also ensure your iframe has sandbox='allow-same-origin allow-scripts allow-popups allow-forms allow-pointer-lock' attributes.

### Mural REST API returns 403 Forbidden when fetching murals

Cause: Mural's REST API requires a Business or Enterprise plan. Free and Team plan accounts receive a 403 error on all API endpoints. Additionally, Personal Access Tokens are scoped to specific workspaces — using a token from workspace A to access workspace B returns 403.

Solution: Verify your Mural account is on the Business or Enterprise plan. If it is, check that MURAL_WORKSPACE_ID matches the workspace where the API token was created — tokens are workspace-specific. Regenerate your token in the correct workspace's developer settings.

### Created mural shows blank or loading state when embedded immediately after creation

Cause: Mural takes a few seconds after the POST /murals API call to fully initialize the new whiteboard. If you embed the share link immediately in the API response, the iframe may show a loading screen because the board is not yet ready.

Solution: Add a brief delay (2-3 seconds) before redirecting users to the new mural embed, or implement a polling check using the GET /murals/{muralId} endpoint until the status changes from 'creating' to 'active'. For most use cases, a simple 3-second delay with a loading spinner is sufficient.

```
// Simple delay before embedding a newly created mural
await new Promise(resolve => setTimeout(resolve, 3000));
// Now safe to embed the mural
```

## Frequently asked questions

### Do I need a paid Mural plan to use Mural in Bolt.new?

Not for the embed approach. Embedding existing murals via iframe works on any Mural plan including Free, since it just uses the share link you generate from the Mural interface. The REST API (creating murals programmatically, listing workspace murals) requires a Business or Enterprise plan. If you only need to embed specific known murals in your dashboard, the free plan is sufficient.

### Can I use the Mural API from Bolt.new's WebContainer during development?

Yes. All Mural REST API calls are outbound HTTP requests to app.mural.co. These work in Bolt.new's WebContainer since they use standard HTTPS fetch calls. There is no incoming connection requirement for the Mural integration — you are always calling Mural's API, not the other way around. This means you can develop and test the full Mural integration in Bolt without deploying first.

### How do I get a Mural API token?

Go to your Mural workspace settings and navigate to Settings → Developers → API Access. Click 'Create Application' or 'Generate Personal Access Token'. The token is a long JWT string that starts with 'eyJ'. Mural's developer API is at app.mural.co/api for documentation. Note that API access is only available on Business and Enterprise plans — the option may not appear in your settings if you are on a Free or Team plan.

### Why does my Mural embed show a login screen even though I set Visitor access?

Check that you are using the embed URL (contains /embed/ in the path) rather than the regular share URL. In Mural, click Share → Get embed link to get the correct embed URL — do not use the standard Copy Link URL in an iframe. Also verify the iframe has sandbox='allow-same-origin allow-scripts allow-popups allow-forms allow-pointer-lock' — without allow-same-origin, Mural's session check fails and it redirects to login.

### How do I find my Mural workspace ID and room ID?

Your workspace ID is visible in the Mural URL when you are logged in: https://app.mural.co/t/{workspaceId}/. Room IDs appear in room URLs: https://app.mural.co/t/{workspaceId}/r/{roomId}. Navigate to the room in the Mural sidebar, check the URL, and copy the ID segment. Both values are also available via the Mural API: GET /workspaces to list workspaces and GET /workspaces/{workspaceId}/rooms to list rooms.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/mural
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/mural
