From website form to a live Power BI dashboard
Form submits, row lands in an Excel table in OneDrive, Power BI picks it up on the next refresh. No exports, no copy-paste, no touching the report again.
The architecture: why an Excel table is the right hinge
Power BI is happiest reading from a structured, named source that grows in place - and an Excel table in OneDrive is exactly that. So the pipeline this guide builds has three stages: your form posts to a SheetLink Forms endpoint, accepted submissions are inserted as live rows into an Excel table in your OneDrive workbook via Microsoft Graph, and Power BI reads that table and refreshes on a schedule.
The middle stage is what makes the whole thing hands-off. SheetLink writes real table rows as submissions arrive - not a CSV you download, not an export you remember to run - so the workbook Power BI is connected to is always the current state of your leads. Because the destination is a table (the structured object, not a loose range), Power BI's navigator sees it by name and every downstream visual picks up new rows automatically on refresh. The destination itself is covered in depth on the Excel Online destination page; this guide focuses on the Power BI half.
Step 1: create the Excel table with dashboard-ready columns
In Excel Online (a Microsoft 365 account or a personal Microsoft account with OneDrive), create a workbook and type a header row. The columns you choose here are the columns your dashboard gets, because the table's columns define the row shape - fields are matched to headers by name, and a field with no matching column is not written.
A layout that serves a marketing dashboard well:
Name | Email | Message | submitted_at | utm_source | utm_medium | utm_campaign | gclid | fbclid | msclkidSelect the header row, choose Insert -> Table with "My table has headers" checked, then connect Microsoft 365 in the SheetLink dashboard and pick this workbook and table as your form's destination. The full setup walkthrough, including the table model and its edge cases, is in the guide on sending form submissions to Excel Online - this guide will not repeat it.
One column deserves a note: submitted_at. Time-series visuals need a date, and the cleanest way to guarantee one is to send it as a field from the page, which the next step covers.
Step 2: wire the form - any ingestion path feeds the same table
The dashboard does not care how submissions arrive, because every path lands in the same table through the same pipeline. Pick whichever fits your site:
- Plain HTML form - point the
actionat your endpoint. Walkthrough: HTML form to Excel. - The
sl.jsembed - same form, plus AJAX submits and automatic attribution capture (more below). - Webflow - one site-wide webhook covers every form: Webflow forms to Excel.
- Framer - the Form component posts to your
/w/framer/webhook URL. - Email-in - forward any notification email to your form's address and Label: value lines become rows.
For the HTML path, a form that also stamps the submission time looks like this:
<form action="https://sheetlinkforms.com/f/slf_yourtoken" method="POST">
<input name="name" placeholder="Name" required>
<input name="email" type="email" placeholder="Work email" required>
<input type="hidden" name="submitted_at" id="slf-ts">
<input name="_slhp" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">
<button type="submit">Send</button>
</form>
<script>
document.getElementById("slf-ts").value = new Date().toISOString();
</script>The hidden submitted_at field fills the timestamp column your date axis will use, and the _slhp input is the spam honeypot - keep it in every form you ship.
Step 3: capture attribution, or the dashboard has nothing to slice by
A submissions-per-day line is nice; submissions-per-day-per-campaign is a dashboard. The slicing dimensions come from the attribution columns, and the sl.js embed fills them without any hidden-field wiring:
<script src="https://sheetlinkforms.com/sl.js" data-token="slf_yourtoken"></script>On every page load it captures utm_source, utm_medium, utm_campaign, utm_term, and utm_content, plus the ad click IDs gclid, wbraid, gbraid, fbclid, and msclkid, persists them in localStorage across pages, and attaches them at submit time - so a visitor who lands from an ad, browses, and converts three pages later still carries the click into the row. Add matching columns to the table (as in Step 1) and each value lands in its cell.
The mechanics, the multi-page problem, and the iOS wbraid/gbraid subtlety are covered in capturing gclid and every other click ID - worth reading before you trust the numbers, because attribution gaps look like organic traffic, not like errors.
Step 4: connect Power BI Desktop to the OneDrive workbook
With rows flowing, open Power BI Desktop and connect to the workbook where it lives - in OneDrive, not a local copy, because the service-side refresh in Step 6 depends on that.
The connection method that behaves best: open the workbook in Excel Online, choose File -> Info -> Copy path, and you get a direct URL to the file. Strip the ?web=1 suffix from the end, then in Power BI Desktop use Get Data -> Web and paste that URL, signing in with the same Microsoft account. Power BI treats it as an Excel source and shows the Navigator.
In the Navigator you will see the workbook's objects - and this is where the table pays off again: your submissions table appears by name as a proper table object, not a guessed cell range. Tick it, choose Transform Data (not Load - Step 5 explains why), and you are in Power Query with live form data.
If you just want to explore locally first, Get Data -> Excel workbook against a synced local copy works too - but expect to redo the connection the OneDrive way before publishing, so refresh works in the service.
Step 5: shape the data in Power Query
Three transformations turn raw submission rows into dashboard-ready data:
- Set the types. Make
submitted_ata Date/Time column (then add a Date-only column from it for daily grouping). Leave the click ID columns as text. - Derive a Channel column. Add a Conditional Column: if
gclidis not empty then "Google Ads", else iffbclidis not empty then "Meta", else ifmsclkidis not empty then "Microsoft Ads", else "Organic / direct". Click-ID presence is a more honest paid-vs-organic split than UTMs alone, because UTMs depend on humans tagging links correctly. - Tidy the text dimensions. Trim and lowercase
utm_sourceandutm_campaignso "Newsletter" and "newsletter" do not chart as two sources.
One habit that saves pain later: do renames and type changes in Power Query, not by editing the Excel table's headers. The table's headers are the contract your form fields map against - rename a header in Excel and the automatic field mapping for that column breaks until you fix the field name or add an explicit mapping.
Step 6: build the visuals
A layout that answers the questions marketing actually asks, using only the columns this guide set up:
- Submissions over time: a line or column chart of row count by date - the heartbeat of the funnel.
- Leads by source: a bar chart of count by
utm_source, withutm_campaignas a drill-down level. - Paid vs organic: a donut of the derived Channel column - the click-ID-presence split from Step 5.
- Cards: total submissions, submissions this week, and share with a
gclidpresent (a quick read on how much of the funnel is paid Google). - Recent leads table: name, email, source, and date, sorted newest first - the tab people actually open.
Add a relative date slicer (last 30 days is a sane default) and the report stays focused without anyone editing filters.
Step 7: publish and set up refresh - the honest part
Publish the report to the Power BI service (Home -> Publish). Now the freshness question, stated plainly, because this is where expectations go wrong:
- In Power BI Desktop, refresh is manual. You click Refresh, it re-reads the workbook. Nothing updates on its own.
- In the service, a semantic model connected to a workbook in OneDrive can refresh from OneDrive automatically - roughly hourly. New form rows appear in the published report within about an hour of landing in the table, with no action from you.
- Scheduled refresh is the alternative: configure refresh times on the semantic model in the service. Per Microsoft's documentation, Pro-licensed models are limited to 8 scheduled refreshes per day, so this path is coarser than the OneDrive sync, not finer.
The net: this pipeline gives you a dashboard that is current to within about an hour, hands-off, indefinitely. That is exactly right for marketing and lead reporting. It is not a second-by-second ops wallboard, and pretending otherwise sets someone up to distrust the numbers - if a stakeholder needs to see a specific submission right now, the Excel workbook itself is the live view: rows land there within moments of an accepted submission.
End-to-end latency, added up
Worth being precise about, because two refresh cycles stack:
- Form to table: delivery is asynchronous and direct via Microsoft Graph - typically moments after an accepted submission. If Graph is briefly unreachable, retries run at 5 minutes, 30 minutes, and 2 hours, and every attempt is recorded in the delivery log.
- Table to dashboard: whenever the service next refreshes - roughly hourly on the OneDrive path.
So the typical worst case from submit to published visual is a bit over an hour, and the common case is well under. One more thing the pipeline buys you here: spam screening happens before the row lands, and suspicious submissions wait in quarantine for one-click review rather than being written into your data. Your dashboard counts screened leads, not raw traffic - which means the numbers you present are the numbers you would defend.
Troubleshooting
- New submissions are not in the report: check in order - is the row in the Excel table (if not, check the delivery log, then the quarantine queue in the SheetLink dashboard); has the service refreshed since the row landed (check the semantic model's refresh history); did you refresh manually if you are looking in Desktop?
- The table does not appear in Power BI's Navigator: the data was never formatted as a table, or you connected to a copy of the file. Convert the range with Insert -> Table in Excel, and confirm the URL came from File -> Info -> Copy path on the OneDrive workbook.
- Refresh fails with a credentials error: the OAuth token the service holds for the data source has expired - re-enter credentials on the semantic model's data source settings.
- Dates load as text: set the type in Power Query (Step 5). ISO timestamps from the hidden
submitted_atfield parse cleanly as Date/Time. - A campaign shows zero leads you know exist: attribution gap, not a delivery gap - usually untagged ad links or a page missing the embed. The checklist in the click ID guide covers the usual leaks.
SheetLink Forms is free during beta, invite required - join the waitlist, or watch rows land live on the demo before wiring your own.
FAQ
Does Power BI see new form submissions automatically?
Yes, on refresh. Because SheetLink inserts rows into a named Excel table, Power BI's query picks up every new row the next time the semantic model refreshes - roughly hourly via OneDrive sync in the service, or whenever you click Refresh in Desktop. You never re-point the connection or edit ranges.
How fresh is the dashboard, really?
Rows reach the Excel table within moments of an accepted submission; the published report then updates on the service's next refresh, which is roughly hourly on the OneDrive path. Budget "current to within about an hour." The Excel workbook itself is the real-time view if someone needs to see a submission immediately.
Do I need a paid Power BI plan?
Power BI Desktop is free, and building plus refreshing locally costs nothing. Publishing to the service and sharing reports with others is governed by Microsoft's Power BI licensing, and scheduled-refresh limits differ by license tier per Microsoft's documentation - check the current terms for your organization rather than trusting a comparison page's snapshot.
Can I do this with Google Sheets instead of Excel?
SheetLink Forms delivers to Google Sheets just as natively, and Power BI can connect to Google Sheets - but the Excel-table-in-OneDrive path is the one Power BI treats as a first-class citizen, with the named table in the Navigator and OneDrive-based refresh in the service. If Power BI is the destination, pick the Excel Online destination for the form.
Will spam submissions pollute my dashboard numbers?
Screening happens before the row is written: honeypot hits are marked spam outright, and suspicious submissions wait in quarantine for one-click review instead of landing in the table. Your dashboard counts rows that passed screening (plus anything you approved), so a bot flood shows up in the quarantine queue, not in your conversion chart.
How do I split paid vs organic leads in the report?
Use click-ID presence. The sl.js embed writes gclid, fbclid, and msclkid (plus wbraid/gbraid for iOS Google traffic) into their columns; in Power Query, derive a Channel column from whichever is non-empty. It is more reliable than UTMs alone, because click IDs are appended by the ad platforms automatically while UTMs depend on humans tagging every link.
What happens if delivery to Excel fails right before a refresh?
The submission is not lost - delivery retries automatically at 5 minutes, 30 minutes, and 2 hours, with each attempt recorded in the delivery log. The row simply lands a little later and appears in the report on a subsequent refresh. A submission is always in exactly one state: delivered, retrying on schedule, or quarantined awaiting review.
Does this work if my site is on Webflow or Framer?
Yes - the ingestion path changes, the pipeline does not. Point Webflow's site-wide form webhook at your /w/webflow/ URL (setup at Webflow forms to Excel) or Framer's Form component at your /w/framer/ URL, and submissions land in the same Excel table with the same screening. Everything from Step 4 onward is identical.
Related guides: Send form submissions to Excel Online automatically · Capture gclid (and every other click ID) in your forms · Forms to a spreadsheet without Zapier (and why you might want that)