> ## Content Index
> Fetch the complete content index at: https://www.vizney.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Permissions and Admin Approval Required to Publish a Tableau Data App
- URL: https://www.vizney.com/tableau-data-app-publish-permissions/
- Published: 2026-11-07T10:55:21.000Z
- Updated: 2026-11-07T11:36:19.000Z
- Author: Yehwan Hwang
- Tags: Knowledge Base

[Tableau Data Apps](https://www.vizney.com/what-is-tableau-data-apps/) let you package a code-built analytics web app in a workbook and have Tableau host it. Apps that call external APIs need an administrator to allow those origins. So we tested, from scratch to publishing on Tableau Cloud, whether an app that skips external calls and uses only `queryAsync()` can be published without admin approval.

It published and ran without any external-origin approval (as the publishing owner). But API Access on the data source was a separate requirement. The shared data source in the Samples project lacked that permission, so we were blocked at the query stage, before the app was built, and switched to a data source we published ourselves. You can't conclude that "`queryAsync()` alone means no approval needed."

Below, \[Verified\] means we confirmed it directly, \[Docs\] means official documentation, and \[Inferred\] means a judgment we didn't test.

- **External origins:** No approval needed. The allowed-origin list was empty (`allowedOrigins: []`), and the app code made no `fetch()` calls. \[Verified\]
- **API Access on the data source:** Required. Queries against the shared data source were refused, and our account couldn't grant the permission. \[Verified\] The official docs also require API Access on each data source for creators. \[Docs\]
- **Publishing the workbook:** Succeeded without approval. \[Verified\]
- **Running the app:** It ran with no admin action and no trust prompt (owner account). \[Verified\] Data apps are enabled per site. \[Docs\] Since the app ran, the site appears to allow them, but we didn't check the settings page. \[Inferred\]
- **Other viewers:** They need API Access on both the workbook and the data source. \[Docs\] We couldn't test with another account.

Test environment:

- **Site:** Tableau Cloud 2026.3.3
- **Tools:** Claude Code, Tableau MCP, and the data app authoring skill in tableau-plugin (commit `a8febd1`)
- **Local machine:** Windows, Python 3.14.7
- **App:** two KPIs (sum of Sales and Profit), a Sales-by-Region bar chart, and a Region filter
- **Extensions API:** 1.19.0-pre.10, per the bundled library
- **Date:** November 6, 2026

---

### 1\. Where did the first attempt fail?

It failed while we were checking the data source's fields, before the app was built. The original target was the shared `Superstore Datasource` in the Samples project. Tableau MCP's `get-datasource-metadata` returned 403, and `query-datasource` returned:

```
The user does not have the required permissions: VIZQL_DATA_API_ACCESS to access the data source.
```

Retrying returned the same 403, both right away and after the user tried to grant the permission. In the end, the user's account couldn't grant API Access.

This requirement is in the [overview docs](https://tableau.github.io/extensions-api/docs/dataapps/trex%5Fdataapp%5Foverview?ref=vizney.com) under creator requirements: "View, Connect and API access to each published data source the data app uses." The plugin's preflight checks (site role, publish permission, tool availability) don't cover data source permissions, though, and the publish summary script lists API Access only as a Viewers condition. So the requirement surfaced at the first query, not at publishing. Before building a data app, confirm that you can read the target data source's metadata.

---

### 2\. How did we proceed without the permission?

We switched to `Superstore_YH`, a data source the user published to the default project. The same tools read its metadata and queried it successfully, and we used `Sales`, `Profit`, and `Region` out of 21 fields. As the publisher, the user likely has API Access as owner, but we didn't check the permission rules. \[Inferred\]

Because this differs from the data source we originally planned to use, every publishing and runtime result here is for `Superstore_YH`. We didn't use workarounds such as querying an embedded data source, downloading the workbook, guessing fields, or substituting a file data source.

---

### 3\. What steps did publishing take?

Publishing took 7 minutes 22 seconds, from switching data sources at 20:32:58 to finishing at 20:40:20\. From the start, it took about 31 minutes, including the time stalled on permissions.

- **Workspace:** `scaffold-data-app` succeeded, and the external-origin list was empty (`allowedOrigins: []`). So the app uses only `queryAsync()` and draws its chart without a library.
- **Applying the plan:** On Windows, `apply_plan.py` rejected valid paths as escaping the unzip directory. We continued with a copy that changes one line, and reported the issue as [tableau/tableau-plugin#35](https://github.com/tableau/tableau-plugin/issues/35?ref=vizney.com). The exact error and fix are in [Fixing Permission, Script, and Publishing Errors When Building Tableau Data Apps](https://www.vizney.com/tableau-error-data-app/).
- **Connecting the data source:** `wire_datasource.py` connected `Superstore_YH`.
- **Pre-checking values:** We ran the app's queries first with Tableau MCP's `query-datasource`: total Sales 2,326,534.35 and total Profit 292,296.81.
- **Packaging:** `package_twbx.py` produced a 74,443-byte .twbx with no warnings.
- **Publishing:** `publish-workbook` rejected the file-path option (`workbookFilePath`). We got an upload slot with `request-workbook-upload`, uploaded the file, and published with `workbookUploadId`, which returned `status: "published"` and `warnings: []`. This is a branch the plugin docs anticipate.

---

### 4\. Did the published app actually work?

Opened as the publishing owner in a signed-in browser, the KPIs, bar chart, and Region filter all worked on live data.

The check right after publishing, though, was misleading. Tableau MCP's `get-view-image` returned a PNG (40,604 bytes) with no error, but the image showed the "Sign in to Tableau Cloud" page. We don't know why. The same view displayed correctly in a signed-in browser, so there's no evidence the app was at fault. Don't treat a successful response as proof that an app works.

The on-screen values matched the pre-checked values:

- **All regions:** Sales 2,326,534, Profit 292,297
- **Sales by Region:** West 739,814, East 691,828, Central 503,171, South 391,722
- **East selected:** Sales 691,828, Profit 94,883, one bar

![The data app with East selected in the Region filter. The KPIs change to Sales 691,828 and Profit 94,883, and only the East bar remains.](https://storage.ghost.io/c/6a/50/6a50f03c-1922-4e90-bf18-c5abd0f819d4/content/images/2026/11/live-filter-east-crop.jpg)

- **Query path:** `queryAsync()` went to `POST /api/v1/vizql-data-service/query-datasource` on the same host. Each page load sent three requests (region list, totals, Sales by Region), all HTTP 200\. \[Verified\]
- **Content Security Policy:** The response header was as follows, with no external origins. `<pkg>` stands for the package's `content/` path. \[Verified\]

```
default-src <pkg>; img-src data: <pkg>; font-src data: <pkg>; script-src 'unsafe-inline' <pkg> 'unsafe-eval'; style-src 'unsafe-inline' <pkg>; frame-src 'none'; object-src 'none'; sandbox allow-scripts allow-popups allow-popups-to-escape-sandbox
```

- **Sandbox:** The app frame lacks `allow-same-origin`, which matches the docs' description of a `null` origin. \[Verified\]
- **Trust prompt:** It didn't appear. The plugin docs say one may appear on first load; we don't know whether other users see it.
- **Keyboard use:** Moving the filter to East with the arrow keys sent four queries. A change event appears to have fired when the selection passed through Central, too. It's not an error, but it matters for apps with a `<select>` filter.

![Hovering over the Central bar shows a 'Central / Sales 503,171' tooltip and highlights the row.](https://storage.ghost.io/c/6a/50/6a50f03c-1922-4e90-bf18-c5abd0f819d4/content/images/2026/11/live-tooltip-crop.jpg)

API Access was needed even without external calls because `queryAsync()` calls VizQL Data Service. [Tableau's help](https://help.tableau.com/current/server/en-us/permissions%5Fcapabilities.htm?ref=vizney.com) describes API Access as the capability that lets a user query data sources through VizQL Data Service.

Some things we couldn't verify:

- **Other viewers:** The publish summary script reported the following. The official docs likewise require View, Full Data Query, and API Access on the workbook, plus View, Connect, and API Access on the data source. We couldn't test another account, so we haven't seen how the app fails for a viewer without API Access.

```
Some viewers may not be able to view this data app. Missing workbook permissions:
- Unnamed group: API Access
Viewers also need API Access on the published data source "Superstore_YH".
```

- **Loading, empty, and error states:** We tested these only in a mock environment that imitates the `tableau` object.
- **API version:** The sandbox kept us from reading the runtime value; per the bundled library it's 1.19.0-pre.10.

In practice, confirm before you start that you can read the target data source's metadata, check API Access on both the workbook and the data source for your viewer groups before you share, and then open the app yourself with a restricted account.

---

### Note:

This post is based on a test on Tableau Cloud (2026.3.3) on November 6, 2026, and on Tableau's official docs as of November 7, 2026\. Features and requirements may change during the beta.