Skip to content
MCP server built inHow that works

HTTP API of the BI feed

The BI feed is not only for Tableau. It is an ordinary read-only HTTP endpoint — and the one supported way to fetch data out of the lakehouse programmatically without stepping around the governance.

On this page

The supported route

The BI feed carries a contract version of its own and stays stable. The other paths under /api/agent/ belong to the console — if you need one of them, let us know and we can talk about it.

Basics

ItemValue
Basehttps://<host>/api/agent/tableau/
Contract version1 — reported by /info, raised only on an incompatible change.
AuthenticationBearer. A Keycloak access token of the realm with audience (aud) agent. Another audience: 401.
MethodsGET only on the data endpoints. The feed accepts no SQL.
CachingEvery response carries Cache-Control: no-store.
AuditingOne WORM entry per fetched page and per schema lookup — without row values, but with the target object and page parameters.

The feed is off as shipped

Letting data out is a deliberate act: until an administrator flips the switch under Daten → BI-Anbindung, the feed answers 403.

The endpoints

EndpointPurposeWho
GET /tableau/infoSelf-description: endpoints, limits, authentication, the JSONPath to the records.Any signed-in user
GET /tableau/schemaAn object's columns with Trino type, Tableau type and the projection used to convert them.Anyone allowed to see the object
GET /tableau/rowsOne page of data, as JSON or CSV.Anyone allowed to see the object
GET /tableau/settingsState of the switch, the row cap and the catalog allowlist.Platform administrators
PUT /tableau/settingsChange those settings. Audited.Platform administrators

Fetching an object's columns

The schema endpoint says what can be delivered from an object at all. Columns whose types the target tool does not know are omitted rather than guessed — they are listed in unsupportedColumns. The recommendedOrderBy field names a usable sort column for paging.

curl -sS \
  -H "Authorization: Bearer $TOKEN" \
  "https://lakehouse.example.invalid/api/agent/tableau/schema\
?catalog=iceberg&schema=produkte&table=produkte" | jq .

Fetching data page by page

# Erste Seite. Ab der zweiten ist orderBy Pflicht – deshalb gleich setzen.
curl -sS \
  -H "Authorization: Bearer $TOKEN" \
  "https://lakehouse.example.invalid/api/agent/tableau/rows\
?catalog=iceberg&schema=produkte&table=produkte\
&limit=1000&offset=0&orderBy=artikelnummer" | jq '.meta, .data[0]'

# Als CSV, etwa zum Weiterreichen an ein Skript
curl -sS \
  -H "Authorization: Bearer $TOKEN" \
  "https://lakehouse.example.invalid/api/agent/tableau/rows\
?catalog=iceberg&schema=produkte&table=produkte\
&limit=1000&orderBy=artikelnummer&format=csv" > produkte.csv

Parameters of /tableau/rows

ParameterRequiredMeaning
catalog, schema, tableyesThe object — table or view.
limitnoRows per page. Default 10,000, hard cap 50,000, lowerable by setting.
offsetnoFirst row of the page.
orderBywhen offset > 0column, column:desc, several comma-separated, or * for all columns.
formatnojson (default) or csv.

Without orderBy, the second page does not come back unsorted — it does not come back at all: 400 order_required. OFFSET without ORDER BY is unstable in a parallel engine; a page could return rows twice and skip others without any error surfacing.

Response shape

The records sit under data — the same JSONPath Tableau reads. Alongside it, meta describes the page; if you only want the rows, ignore it.

GET /tableau/rows (abridged)
{
  "data": [
    { "artikelnummer": "A-1001", "bezeichnung": "Rohrschelle 20 mm", "lagerbestand": 412 }
  ],
  "meta": {
    "object": { "catalog": "iceberg", "schema": "produkte", "table": "produkte" },
    "columns": [
      { "name": "artikelnummer", "tableauType": "string" },
      { "name": "lagerbestand", "tableauType": "int" }
    ],
    "unsupportedColumns": [],
    "limit": 1000,
    "offset": 0,
    "rowCount": 1000,
    "hasMore": true,
    "order": ["artikelnummer"],
    "orderAll": false,
    "deterministicPaging": true
  }
}

hasMore needs no counting query

It is derived from rowCount === limit. A loop that checks it is always right — the last page may simply come back empty once.

Error codes

ResponseMeaningRemedy
401Missing or expired token, or wrong audience.Renew the token; aud must be agent.
403 bridge_disabledThe feed is switched off.Administrator: console → BI-Anbindung.
403 catalog_not_allowedThe catalog is not on the allowlist.Administrator: maintain the allowlist.
404 not_foundThe object does not exist — or is not visible to you.Check the grants in Trino. The two look identical on purpose, so the answer reveals nothing about objects you may not see.
400 order_requiredPaging without a sort key.Set orderBy.
400 invalid_pagelimit, offset or orderBy out of bounds.Respect the limits — limit is not silently truncated.
400 query_errorTrino could not run the query.Read the message; usually an exotically typed field. Create a view with a converted column.
422 no_deliverable_columnsNo column of the object can be delivered.Create a view with converted columns.
502 upstream_errorTrino or the token exchange was unreachable.Check operations. Details are in the service log, not in the response.

Getting a token

The feed issues no tokens of its own — it verifies the realm's. For a one-off fetch, an access token from a signed-in session is enough. For scheduled fetches, an administrator creates a dedicated public client in the realm and lets the calling tool run the authorization code flow with PKCE; that client needs an audience mapper to agent. GET /tableau/info reports the authorization and token endpoint addresses.

A token carries an identity

There is no service account here either: a token carries one person's rights — and their name on every audit entry.

Applies to: This page describes lavalake 0.16.0. Product and documentation live in separate repositories — when in doubt, what the console shows is authoritative.

Something missing?

If these pages do not answer your question, we will walk through the platform against your own case in half an hour.

Book a demo