Integrate Mural with Lovable by building a Supabase Edge Function that proxies the Mural REST API using OAuth2 Bearer token authentication. Store credentials in Cloud Secrets, create an Edge Function to fetch murals, rooms, and widget content, then prompt Lovable to build collaboration dashboards that embed boards as iframes and sync whiteboard outputs — sticky notes, frameworks, and voting results — into structured application data.
Sync Mural Whiteboard Outputs and Embed Boards in Lovable Apps
Mural is the collaborative whiteboard platform of choice for design thinking workshops, retrospectives, and distributed team planning sessions. Its strength is in facilitated collaboration: teams work together on sticky note clusters, voting rounds, timer-based activities, and structured frameworks like the Business Model Canvas or Design Sprint templates. The output of these sessions — clustered ideas, voted priorities, documented decisions — is highly valuable but typically stays locked in the Mural board where only participants can access it.
Integrating Mural with Lovable bridges the gap between visual collaboration and structured data. You can embed a live Mural board in your app so stakeholders can view the latest state of a planning session without needing a Mural account. More powerfully, you can extract the text content of sticky notes and widget labels from a board via the API, structure it as data in Supabase, and surface it in actionable dashboards — turning a retrospective's insights into tracked action items, or turning a prioritization vote's results into a ranked feature backlog.
The Mural REST API provides endpoints for listing workspaces and rooms, fetching mural metadata and embed URLs, and retrieving widget (sticky note, text, shape) content from a board. Combined with Mural's viewer embed URL pattern, you have two complementary integration modes: visual embedding for collaboration viewing, and data extraction for turning whiteboard work into structured application content.
Integration method
Mural has no native Lovable connector, so all API operations are handled through a Supabase Edge Function proxy running on Deno. The Edge Function authenticates with the Mural REST API using OAuth2 Bearer tokens, fetches mural metadata and widget content from boards, and returns structured data to your Lovable frontend. Published murals can also be embedded as iframes using Mural's viewer embed URL for direct whiteboard display without API complexity.
Prerequisites
- A Mural account (Team or Enterprise plan for API access — the API is not available on the free plan)
- A Mural developer application created at developers.mural.co to obtain OAuth2 client credentials
- At least one Mural board (mural) with content to work with
- A Lovable project with Lovable Cloud enabled
- Basic understanding of the Mural workspace structure: Workspaces → Rooms → Murals → Widgets
Step-by-step guide
Create a Mural developer application and obtain OAuth2 credentials
Create a Mural developer application and obtain OAuth2 credentials
The Mural REST API requires OAuth2 authentication. You need to register a developer application to get a client ID and client secret. Navigate to developers.mural.co in your browser and sign in with your Mural account. Click 'Create application' or navigate to the Applications section of the developer portal. Fill in the application details: give it a name like 'Lovable Integration', set a description, and for the OAuth2 redirect URI enter your Lovable project's URL followed by /auth/mural/callback — for example https://your-project.lovable.app/auth/mural/callback. For the Edge Function pattern you are building, you may also use a placeholder URL since you will be generating an access token manually for initial testing. After creating the application, note the Client ID and Client Secret. Also take note of the OAuth2 token endpoint: https://app.mural.co/api/public/v1/authorization/oauth2/token. For a personal single-user integration, you can generate a personal access token from your Mural account settings (Account Settings → Developer) rather than implementing the full OAuth2 authorization code flow. A personal access token is simpler and works like a Bearer token directly. Store all credentials immediately in a secure location — you will add them to Cloud Secrets in the next step.
Pro tip: Mural's API is only available on paid plans (Team and Enterprise). If you are on the free plan, you will not be able to access the API. Check your plan at app.mural.co/settings/billing before proceeding with this integration.
Expected result: You have a Mural OAuth2 Client ID and Client Secret (or a personal access token), and you understand which murals are in your workspace that you want to integrate.
Store Mural credentials in Cloud Secrets
Store Mural credentials in Cloud Secrets
Add your Mural credentials to Cloud Secrets so the Edge Function can authenticate with the Mural API server-side. Open the Cloud tab in your Lovable project by clicking the '+' icon next to the Preview panel at the top. Navigate to the Secrets section in the Cloud tab sidebar. If you are using a personal access token (simpler approach), add a single secret: MURAL_ACCESS_TOKEN with your personal token as the value. If you are using OAuth2 client credentials, add two secrets: MURAL_CLIENT_ID and MURAL_CLIENT_SECRET. Also add MURAL_WORKSPACE_ID — your Mural workspace ID, visible in the URL when you are logged into Mural (app.mural.co/t/WORKSPACE-ID/). This is the URL slug that identifies your team workspace, not a numeric ID. Lovable's security infrastructure scans all application code for hardcoded API keys and blocks approximately 1,200 per day. Always use Cloud Secrets for Mural credentials — never include access tokens in React component code or Lovable chat prompts where they could be exposed.
Pro tip: Mural personal access tokens can be generated from app.mural.co settings under Developer → Personal Access Tokens. They are simpler to use than the full OAuth2 client credentials flow and work for single-user integrations where you are accessing your own workspace.
Expected result: MURAL_ACCESS_TOKEN (or MURAL_CLIENT_ID and MURAL_CLIENT_SECRET) plus MURAL_WORKSPACE_ID are stored in Cloud → Secrets, encrypted and ready for Edge Function access.
Create the Mural Edge Function proxy
Create the Mural Edge Function proxy
Build the Edge Function that authenticates with the Mural API and exposes the operations your Lovable frontend needs. The Mural API base URL is https://app.mural.co/api/public/v1. All requests use Bearer token authentication — pass the token in the Authorization header. The Edge Function needs to support three core operations: listing murals in a room, fetching widgets (sticky notes, text boxes, shapes) from a specific mural, and getting rooms in the workspace. Widget data is the most valuable for data extraction use cases since it contains the text content of sticky notes and other board elements. The Mural widgets endpoint returns an array of all elements on a board with their type, position, size, text content, and properties like background color and vote count. Your frontend can filter this array to find specific types or those within specific frames.
Create a Supabase Edge Function called 'mural-proxy' at supabase/functions/mural-proxy/index.ts. Use MURAL_ACCESS_TOKEN and MURAL_WORKSPACE_ID from Deno.env.get(). The Mural API base URL is https://app.mural.co/api/public/v1. Authenticate all requests with 'Authorization: Bearer {token}'. Accept POST requests with action and optional payload. Implement: 'list_rooms' — GET /workspaces/{workspaceId}/rooms returning room id, name, and type; 'list_murals' — GET /workspaces/{workspaceId}/murals or GET /rooms/{roomId}/murals if payload.room_id provided, returning mural id, title, createdOn, updatedOn, and shareLink; 'get_mural' — GET /murals/{muralId} from payload.mural_id returning full mural details; 'get_widgets' — GET /murals/{muralId}/widgets returning all widgets with id, type, text (or title for stickies), x, y, width, height, backgroundColor, votes. Include CORS headers and error handling.
Paste this in Lovable chat
1// supabase/functions/mural-proxy/index.ts2import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';34const corsHeaders = {5 'Access-Control-Allow-Origin': '*',6 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',7};89serve(async (req) => {10 if (req.method === 'OPTIONS') {11 return new Response('ok', { headers: corsHeaders });12 }1314 try {15 const token = Deno.env.get('MURAL_ACCESS_TOKEN');16 const workspaceId = Deno.env.get('MURAL_WORKSPACE_ID');1718 if (!token || !workspaceId) {19 return new Response(20 JSON.stringify({ error: 'MURAL_ACCESS_TOKEN or MURAL_WORKSPACE_ID not configured' }),21 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }22 );23 }2425 const { action, payload = {} } = await req.json();26 const base = 'https://app.mural.co/api/public/v1';27 const headers = {28 Authorization: `Bearer ${token}`,29 'Content-Type': 'application/json',30 Accept: 'application/json',31 };3233 let url = '';34 switch (action) {35 case 'list_rooms':36 url = `${base}/workspaces/${workspaceId}/rooms`;37 break;38 case 'list_murals':39 url = payload.room_id40 ? `${base}/rooms/${payload.room_id}/murals`41 : `${base}/workspaces/${workspaceId}/murals`;42 break;43 case 'get_mural':44 url = `${base}/murals/${payload.mural_id}`;45 break;46 case 'get_widgets':47 url = `${base}/murals/${payload.mural_id}/widgets`;48 break;49 default:50 return new Response(51 JSON.stringify({ error: `Unknown action: ${action}` }),52 { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }53 );54 }5556 const res = await fetch(url, { headers });57 const data = await res.json();5859 return new Response(JSON.stringify(data), {60 status: res.status,61 headers: { ...corsHeaders, 'Content-Type': 'application/json' },62 });63 } catch (err) {64 return new Response(65 JSON.stringify({ error: err.message }),66 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }67 );68 }69});Pro tip: The Mural widgets endpoint can return hundreds or thousands of widgets for large boards. Implement pagination using the cursor or offset parameters in the API request, and consider filtering widget types on the Edge Function side before returning data to reduce response payload size.
Expected result: The mural-proxy Edge Function is deployed and visible in Cloud → Logs. The list_murals action should return your actual Mural boards when called from a test request.
Build the whiteboard viewer and data extraction interface
Build the whiteboard viewer and data extraction interface
With the Edge Function deployed, ask Lovable to build both the embedding interface and the data extraction view. For the embedding, Mural provides viewer URLs in the format https://app.mural.co/t/WORKSPACE/m/WORKSPACE/MURAL-ID/view — when public viewing is enabled on a board, this URL can be placed in an iframe for live board viewing. For data extraction, the widget API fetches all elements on a board and your React components can filter and display them as structured content. The most common extraction pattern is filtering for sticky notes (type 'sticky note') and grouping them by their parent frame — this reconstructs the cluster structure visible on the Mural board. Ask Lovable to build a page that combines both: an embedded viewer tab for visual reference and a data tab that extracts and structures the widget content for actionable use.
Build a Mural Board Viewer page with two tabs: 'Visual' and 'Data'. Visual tab: show the selected mural embedded as an iframe. List murals from 'list_murals' Edge Function action in a dropdown at the top to select which board to view. Use the mural's shareLink from the API response as the iframe src. Make the iframe fill the available height with a minimum of 600px. Data tab: call 'get_widgets' for the selected mural. Filter widgets where type is 'sticky note' or 'text'. Group them by their parentId (frame/group parent). Display each group as a collapsible section with the group name as the header. Within each group show sticky note cards with the text content and background color. Add a vote count badge if votes > 0. Include a 'Save to Supabase' button per group that stores the sticky notes as rows in a workshop_items table.
Paste this in Lovable chat
Pro tip: For complex Mural data extraction workflows — including multi-step workshop processing, automated retrospective action tracking, or team productivity analytics built on Mural data — RapidDev's team can help design the full data pipeline architecture.
Expected result: A Mural board viewer page shows your murals in a dropdown. The Visual tab embeds the selected board and the Data tab displays extracted sticky note content grouped by frame.
Common use cases
Design sprint outcomes tracker with voting results
Build an outcomes page that pulls voting data from a Mural design sprint board, showing which ideas received the most votes. After a design sprint session, the Edge Function fetches widgets from the sprint board, filters for sticky notes with vote counts, and displays them as a ranked priority list in Lovable. This turns ephemeral workshop results into a persistent, shareable decisions record.
Create a Design Sprint Outcomes page that calls the mural-proxy Edge Function with action 'get_widgets' for a specific mural ID stored in the URL params. Filter the widgets for sticky notes (type 'sticky note') and sort them by their vote count in descending order. Display them as ranked cards with the sticky note text, vote count badge, and the sticky note's background color preserved. Group them by their cluster/frame parent if available. At the top show the mural title and when the session was last updated. Add an 'Export to CSV' button that downloads the ranked list.
Copy this prompt to try it in Lovable
Retrospective actions extractor and task tracker
Create a retrospective integration that fetches sticky notes from a Mural retrospective board and converts them into action items stored in Supabase. After each sprint retrospective, team members run the sync to pull all 'Action Items' sticky notes from the designated Mural frame into a task list. Each action item gets an owner assigned and a due date, and the status tracks whether the action was completed before the next retrospective.
Build a Retrospective Actions page. Add a 'Sync from Mural' button that calls the mural-proxy Edge Function with action 'get_widgets' for the configured retro board ID. Filter widgets that are inside a frame labeled 'Action Items'. For each sticky note found, show a dialog to assign an owner (from a Supabase team members list) and due date. When confirmed, save to a Supabase retro_actions table with fields: text, owner_id, due_date, mural_widget_id, status (open/done), and retro_date. Display all saved actions as a kanban board with Open and Done columns. Include a 'Mark Done' button on each action card.
Copy this prompt to try it in Lovable
Team workshop library with embedded board viewer
Build a workshop archive where each past Mural session is catalogued with its embed link, date, participants, and a summary. The boards are embedded as viewer iframes so team members can review past sessions without a Mural account. A sidebar lists workshops by date and type (retro, design sprint, planning), making it easy to find and revisit past collaboration sessions.
Create a Workshop Library page. Store workshop metadata in a Supabase workshops table with fields: title, date, type (retrospective/design-sprint/planning), participants (text array), summary, and mural_embed_url. Display workshops as a timeline list on the left sorted by date. Clicking a workshop shows the embedded Mural board on the right using the mural_embed_url in a responsive iframe. Below the embed show the workshop summary and participant list. Add a 'Add Workshop' button that opens a form to register a new workshop — include a field for the Mural embed URL which can be found in the Mural board's Share settings.
Copy this prompt to try it in Lovable
Troubleshooting
Edge Function returns 403 Forbidden when calling the Mural API
Cause: The access token in MURAL_ACCESS_TOKEN has expired, been revoked, or the account associated with the token does not have API access on its current plan. Mural API access requires a Team or Enterprise plan subscription.
Solution: Generate a new personal access token from app.mural.co Settings → Developer → Personal Access Tokens. Update the MURAL_ACCESS_TOKEN secret in Cloud → Secrets with the new value. Verify your Mural account is on a plan that includes API access. Personal access tokens in Mural have configurable expiration periods — check the expiry date of your token.
list_murals returns an empty array even though murals exist in the workspace
Cause: The MURAL_WORKSPACE_ID does not match the actual workspace ID, or the authenticated user does not have access to the rooms where murals are stored. Mural's workspace ID is the URL slug visible in the workspace URL, not a numeric ID.
Solution: Open Mural in your browser and look at the URL: it will be in the format app.mural.co/t/WORKSPACE-SLUG/. Copy the WORKSPACE-SLUG portion exactly and update the MURAL_WORKSPACE_ID secret. If murals are in private rooms, ensure the authenticated user has been added to those rooms with appropriate access permissions.
Embedded Mural iframe shows a login screen instead of the board
Cause: The mural does not have public viewing enabled. Mural boards are private by default and require a Mural account to view. The viewer embed URL only works for boards where the owner has explicitly enabled visitor access.
Solution: In Mural, open the board, click Share in the top-right corner, and enable 'Anyone with the link can view' or set it to 'Visitor access'. Copy the updated share or embed URL and use that in the iframe src. Note that even with visitor access enabled, some Mural templates and boards may restrict embedded viewing based on workspace security settings.
get_widgets returns widget objects but the text content is in different fields for different widget types
Cause: Mural widgets use different field names for text content depending on the widget type. Sticky notes use the 'title' field for their main text, text boxes use 'text', and shape labels use a 'text' property nested inside a content object. Inconsistent field names across widget types cause some widgets to display as empty.
Solution: Normalize widget text extraction in the Edge Function or frontend by checking multiple fields: use widget.title || widget.text || widget.content?.text || '' as the text extraction strategy. Filter widgets by type first (sticky_note, text, shape) before applying type-specific field extraction logic.
1const widgetText = widget.title ?? widget.text ?? widget.content?.text ?? '';Best practices
- Always store Mural access tokens in Cloud → Secrets, never in React components or Lovable's chat prompt — tokens grant access to all boards in your workspace.
- Enable public viewer access on Mural boards before using iframe embedding — private boards show a login screen in the iframe and cannot be displayed to unauthenticated users.
- Filter widgets by type in your frontend before rendering — large murals can have hundreds of widgets and processing them all in React will cause performance issues. Request only the widget types you need.
- Use frame (parent) grouping when displaying extracted sticky note content — it reconstructs the thematic clusters that workshop participants created and makes the data far more useful than a flat list.
- Normalize widget text extraction across different widget types since Mural uses different field names (title, text, content.text) for text content depending on the widget type.
- Implement a sync timestamp in Supabase when saving workshop data — subsequent syncs from the same mural should update existing records using the widget ID as the unique key rather than creating duplicates.
- Set up a clear user workflow for the 'save to Supabase' action — always show a preview of what will be saved and require a confirmation step before writing workshop outputs to the database.
Alternatives
Lucidchart is better for structured, precise diagramming like flowcharts and org charts where shapes and connections have specific technical meaning, rather than freeform sticky note collaboration.
Miro is the market leader in digital whiteboarding with a deeper REST API, broader integration ecosystem, and an MCP personal connector available natively in Lovable for build-time context during AI-assisted development.
Notion is a better choice if you want to move workshop outputs directly into a structured knowledge base with database views, kanban boards, and team wiki capabilities rather than a visual whiteboard archive.
Frequently asked questions
Is the Mural REST API available on the free plan?
No, the Mural REST API requires a Team or Enterprise plan subscription. If you are on the free Mural plan, you can still use the iframe embedding approach for boards with visitor access enabled — embedding does not require API authentication. The API calls (listing rooms, fetching widgets, getting mural metadata) require a paid plan with API access enabled.
Can I write content back to Mural from my Lovable app?
Yes, the Mural API supports creating widgets (sticky notes, text boxes, shapes) on existing boards via POST requests to the /murals/{muralId}/widgets endpoint. You can programmatically add sticky notes with specific text, color, and position from your Lovable app. This enables use cases like automatically populating a retrospective board template with team members' input collected in your app before the facilitation session.
How do I get the mural ID for a specific board I want to integrate?
The mural ID is visible in the Mural board URL when you open it in your browser: app.mural.co/t/WORKSPACE/m/WORKSPACE/MURAL-ID/board/... Copy the MURAL-ID segment from the URL. You can also retrieve all mural IDs programmatically using the list_murals action in the Edge Function, which returns the id field for every board in your workspace.
Can I use Mural as an MCP personal connector in Lovable instead of this Edge Function approach?
Yes, Mural is one of Lovable's nine built-in MCP personal connectors available in Settings → Connectors → Personal connectors. The MCP connector is designed for build-time context — it lets Lovable's AI read your Mural boards during development to generate more accurate code. The Edge Function approach described in this guide is for runtime use: your deployed Lovable app fetching and displaying Mural data to end users. Both approaches can be used together.
How do I handle very large Mural boards with hundreds of sticky notes?
The Mural widgets API supports pagination with cursor-based paging. Request widgets in batches by passing a cursor parameter, then fetch subsequent pages until the response indicates no more results. In your Lovable app, consider implementing a 'Load more' pattern rather than loading all widgets at once. For very large boards, also consider using the Edge Function to filter widgets by type on the server side before returning data to the frontend — this reduces payload size significantly.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation