> **Building with AI coding agents?** If you're using an AI coding agent, install the official Scalekit plugin. It gives your agent full awareness of the Scalekit API — reducing hallucinations and enabling faster, more accurate code generation.
>
> - **Claude Code**: `/plugin marketplace add scalekit-inc/claude-code-authstack` then `/plugin install <auth-type>@scalekit-auth-stack`
> - **GitHub Copilot CLI**: `copilot plugin marketplace add scalekit-inc/github-copilot-authstack` then `copilot plugin install <auth-type>@scalekit-auth-stack`
> - **Codex**: run the bash installer, restart, then open Plugin Directory and enable `<auth-type>`
> - **Skills CLI** (Windsurf, Cline, 40+ agents): `npx skills add scalekit-inc/skills --list` then `--skill <skill-name>`
>
> `<auth-type>` / `<skill-name>`: `agentkit`, `full-stack-auth`, `mcp-auth`, `modular-sso`, `modular-scim` — [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Tableau

Connect your agent to browse Tableau workbooks, export dashboards, query data sources, and manage site resources via Personal Access Token.

**Authentication:** API Key
**Categories:** Analytics, Business Intelligence, Data Visualization
## What you can do

Connect this agent connector to let your agent:

- **Browse workbooks and views** — List, search, and retrieve detailed metadata for workbooks, views (sheets and dashboards), and data sources published on a Tableau site
- **Export visualizations** — Download dashboards as PNGs, PDF documents, or Excel crosstab files; download full workbooks as `.twbx` files
- **Query underlying data** — Export view summary data as CSV, or run structured queries against published data sources using the VizQL Data Service API (Tableau Cloud 2024.1+)
- **Monitor jobs** — Poll background job status to track completion of long-running operations
- **Manage the site** — Create and update projects; add and remove users; create, add, and remove groups

## Authentication

Tableau uses **Personal Access Token (PAT)** authentication. You store your PAT credentials in Scalekit once, and Scalekit calls `tableau_auth_signin` to obtain a session token, then refreshes it automatically before every tool call. Your agent code never handles tokens directly — Scalekit injects the current session token as the `X-Tableau-Auth` header on every request.

**How it works:**

1. Store your PAT name, PAT secret, domain, and site content URL in a Scalekit connected account
2. Before each tool call, Scalekit checks if the session token is still valid (with a 5-minute buffer)
3. If the token has expired or is about to expire, Scalekit signs in automatically using the stored PAT credentials via `tableau_auth_signin`
4. The fresh session token is injected as `X-Tableau-Auth` — your code does nothing
5. After sign-in, the **site ID** (site LUID) is stored automatically in the connected account — you do not pass `site_id` to tool calls. Token lifetime is 120 minutes for Tableau Cloud and 240 minutes for Tableau Server.

## Set up the connector

Connect your Tableau Cloud or Tableau Server site to Scalekit so your agent can browse workbooks, query views, export dashboards, and manage users.

Scalekit handles session token management automatically. You store your Personal Access Token (PAT) credentials once, and Scalekit signs in and refreshes the session token before it expires — your code never calls the sign-in endpoint directly.

1. ### Create a Personal Access Token in Tableau

   A Personal Access Token (PAT) is used by Scalekit to sign in on your behalf and keep the session alive automatically.

   - Sign in to your Tableau site.
   - Click your avatar in the top-right corner → **My Account Settings**.
   - Scroll to the **Personal Access Tokens** section.
   - Click **+ Create new token**, give it a name (e.g., `scalekit-agent`), and click **Create**.
   - Copy both the **Token Name** and **Token Secret** — the secret is shown only once.

   > Image: Screenshot

   > note: Find your site content URL
>
> Your site content URL is the identifier in your Tableau browser URL. For
> `https://prod-in-a.online.tableau.com/#/site/mycompany-1234567`, the site content URL is `mycompany-1234567`. Leave it blank if you use the Default site.

2. ### Create a connection in Scalekit

   - In [Scalekit dashboard](https://app.scalekit.com), go to **Agent Auth** → **Create Connection**.
   - Search for **Tableau** and click **Create**.
   - Note the **Connection name** — use this as `connection_name` in your code (e.g., `tableau`).
   - Click **Save**.

   > Image: Screenshot

3. ### Add a connected account

   A connected account links a user in your system to their Tableau PAT credentials. Scalekit uses these to sign in and refresh the session automatically.

   **Via dashboard (for testing)**

   - Open the connection → **Connected Accounts** tab → **Add account**.
   - Fill in:
     - **Your User's ID** — any identifier for this user (e.g., `user_123`)
     - **Server Domain** — your Tableau hostname without `https://` (e.g., `prod-in-a.online.tableau.com`)
     - **PAT Name** — the token name from step 1
     - **PAT Secret** — the token secret from step 1
     - **Site Content URL** — the site identifier from your Tableau URL (leave blank for the Default site)
   - Click **Save**.

   > Image: Screenshot

   **Via API (for production)**

   
     ### Node.js

```typescript
await scalekit.actions.upsertConnectedAccount({
  connectionName: 'tableau',
  identifier: 'user_123',
  credentials: {
    domain: 'prod-in-a.online.tableau.com',
    pat_name: 'scalekit-agent',
    pat_secret: process.env.TABLEAU_PAT_SECRET,
    site_content_url: 'mycompany-1234567', // omit for Default site
  },
});
```

     ### Python

```python
scalekit_client.actions.upsert_connected_account(
    connection_name="tableau",
    identifier="user_123",
    credentials={
        "domain": "prod-in-a.online.tableau.com",
        "pat_name": "scalekit-agent",
        "pat_secret": os.getenv("TABLEAU_PAT_SECRET"),
        "site_content_url": "mycompany-1234567",  # omit for Default site
    },
)
```

   

   > tip: Automatic token refresh
>
> Scalekit refreshes the session token before every tool call using the stored PAT credentials. Tokens are renewed automatically when they are within 5 minutes of expiry — you do not need to manage refresh logic in your code.

## Code examples

Once a connected account is set up with PAT credentials, your agent can call Tableau tools through Scalekit. The session token is managed and injected automatically as the `X-Tableau-Auth` header — your code never handles it directly.

The **site ID** (site LUID) is resolved automatically from the connected account after sign-in. You do not pass `site_id` to tool calls. For proxy API calls that require a site ID in the URL path, call `tableau_session_get` once to retrieve it.

## Get the connected account

  ### Node.js

```typescript

const scalekit = new ScalekitClient(
  process.env.SCALEKIT_ENV_URL,
  process.env.SCALEKIT_CLIENT_ID,
  process.env.SCALEKIT_CLIENT_SECRET
);
const actions = scalekit.actions;

const connectionName = 'tableau';
const identifier = 'user_123';

const { connectedAccount } = await actions.getConnectedAccount({
  connectionName,
  identifier,
});
```

  ### Python

```python
from scalekit.client import ScalekitClient

scalekit_client = ScalekitClient(
    client_id=os.getenv("SCALEKIT_CLIENT_ID"),
    client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
    env_url=os.getenv("SCALEKIT_ENV_URL"),
)
actions = scalekit_client.actions

connection_name = "tableau"
identifier = "user_123"

connected_account = actions.get_connected_account(
    connection_name=connection_name, identifier=identifier
).connected_account
```

## Proxy API calls

Use the Scalekit proxy to call any Tableau REST API endpoint directly. Binary downloads (PNG, PDF, Excel, `.twbx`, `.tdsx`) must use the proxy — use `tableau_session_get` to retrieve the site ID for the URL path:

  ### Node.js

```typescript
// Get site ID once (needed for proxy URL construction)
const session = await actions.executeTool({
  toolName: 'tableau_session_get',
  connectedAccountId: connectedAccount.id,
  toolInput: {},
});
const siteId = session.session.site.id;

// Export a view as PNG
const imageBytes = await actions.request({
  connectionName,
  identifier,
  path: `/api/3.28/sites/${siteId}/views/${viewId}/image`,
  method: 'GET',
  queryParams: { resolution: 'high' },
});

// Export a view as PDF
const pdfBytes = await actions.request({
  connectionName,
  identifier,
  path: `/api/3.28/sites/${siteId}/views/${viewId}/pdf`,
  method: 'GET',
  queryParams: { type: 'a4', orientation: 'landscape' },
});

// Download a workbook (.twbx)
const workbookBytes = await actions.request({
  connectionName,
  identifier,
  path: `/api/3.28/sites/${siteId}/workbooks/${workbookId}/content`,
  method: 'GET',
});

// Download a data source (.tdsx)
const datasourceBytes = await actions.request({
  connectionName,
  identifier,
  path: `/api/3.28/sites/${siteId}/datasources/${datasourceId}/content`,
  method: 'GET',
});
```

  ### Python

```python
# Get site ID once (needed for proxy URL construction)
session = actions.execute_tool(
    tool_name="tableau_session_get",
    connected_account_id=connected_account.id,
    tool_input={},
)
site_id = session.data["session"]["site"]["id"]

# Export a view as PNG
image_response = actions.request(
    connection_name=connection_name,
    identifier=identifier,
    path=f"/api/3.28/sites/{site_id}/views/{view_id}/image",
    method="GET",
    query_params={"resolution": "high"},
)
with open("dashboard.png", "wb") as f:
    f.write(image_response.content)

# Export a view as PDF
pdf_response = actions.request(
    connection_name=connection_name,
    identifier=identifier,
    path=f"/api/3.28/sites/{site_id}/views/{view_id}/pdf",
    method="GET",
    query_params={"type": "a4", "orientation": "landscape"},
)
with open("dashboard.pdf", "wb") as f:
    f.write(pdf_response.content)

# Download a workbook (.twbx)
workbook_response = actions.request(
    connection_name=connection_name,
    identifier=identifier,
    path=f"/api/3.28/sites/{site_id}/workbooks/{workbook_id}/content",
    method="GET",
)
with open("workbook.twbx", "wb") as f:
    f.write(workbook_response.content)

# Download a data source (.tdsx)
datasource_response = actions.request(
    connection_name=connection_name,
    identifier=identifier,
    path=f"/api/3.28/sites/{site_id}/datasources/{datasource_id}/content",
    method="GET",
)
with open("datasource.tdsx", "wb") as f:
    f.write(datasource_response.content)
```

> tip: No auth header needed
>
> The Scalekit proxy automatically injects the `X-Tableau-Auth` session token header. You only provide the path and method.

## Use Scalekit tools

### Browse workbooks and views

  ### Node.js

```typescript
// List all workbooks on the site
const workbooks = await actions.executeTool({
  toolName: 'tableau_workbooks_list',
  connectedAccountId: connectedAccount.id,
  toolInput: {},
});
// workbooks.workbooks.workbook[] — each has id, name, contentUrl, project

// Search for a workbook by name
const found = await actions.executeTool({
  toolName: 'tableau_workbook_search',
  connectedAccountId: connectedAccount.id,
  toolInput: { name: 'SalesReport' },
});

// List all views within a workbook
const workbookId = workbooks.workbooks.workbook[0].id;
const views = await actions.executeTool({
  toolName: 'tableau_workbook_views_list',
  connectedAccountId: connectedAccount.id,
  toolInput: { workbook_id: workbookId },
});
// views.views.view[] — each has id, name, contentUrl
```

  ### Python

```python
# List all workbooks on the site
workbooks = actions.execute_tool(
    tool_name="tableau_workbooks_list",
    connected_account_id=connected_account.id,
    tool_input={},
)
# workbooks["workbooks"]["workbook"] — each has id, name, contentUrl, project

# Search for a workbook by name
found = actions.execute_tool(
    tool_name="tableau_workbook_search",
    connected_account_id=connected_account.id,
    tool_input={"name": "SalesReport"},
)

# List all views within a workbook
workbook_id = workbooks["workbooks"]["workbook"][0]["id"]
views = actions.execute_tool(
    tool_name="tableau_workbook_views_list",
    connected_account_id=connected_account.id,
    tool_input={"workbook_id": workbook_id},
)
# views["views"]["view"] — each has id, name, contentUrl
```

### Sign out

Call `tableau_auth_signout` to invalidate the session token when the agent session ends:

  ### Node.js

```typescript
await actions.executeTool({
  toolName: 'tableau_auth_signout',
  connectedAccountId: connectedAccount.id,
  toolInput: {},
});
// The stored session token is now invalid — Scalekit will refresh on next call
```

  ### Python

```python
actions.execute_tool(
    tool_name="tableau_auth_signout",
    connected_account_id=connected_account.id,
    tool_input={},
)
# The stored session token is now invalid — Scalekit will refresh on next call
```

## Getting resource IDs

Most Tableau tools require one or more resource LUIDs. The **site ID is resolved automatically** by Scalekit after sign-in — you do not pass it to tool calls. Always fetch other IDs from the API — never guess or hard-code them.

| Resource | Tool to get ID | Field in response |
|----------|---------------|-------------------|
| Workbook ID | `tableau_workbooks_list` or `tableau_workbook_search` | `workbooks.workbook[].id` |
| View ID | `tableau_views_list` or `tableau_workbook_views_list` | `views.view[].id` |
| Data Source ID | `tableau_datasources_list` | `datasources.datasource[].id` |
| Project ID | `tableau_projects_list` | `projects.project[].id` |
| User ID | `tableau_users_list` | `users.user[].id` |
| Group ID | `tableau_groups_list` | `groups.group[].id` |
| Job ID | `tableau_job_get` (from background job operations) | `job.id` |
| Site ID (proxy only) | `tableau_session_get` | `session.site.id` |

**Recommended start sequence for any agent session:**

```text
1. tableau_workbooks_list           → discover workbooks
2. tableau_workbook_views_list      → discover views within a workbook
3. tableau_datasources_list         → discover data sources
```

## Tool list

## Tool list

### `tableau_auth_signout`

Sign out of Tableau, invalidating the current session token. Call this at the end of an agent session. Scalekit will obtain a fresh token automatically on the next tool call.

### `tableau_session_get`

Returns information about the current authenticated session, including the site name, site content URL, and the authenticated user. Useful for confirming which site the agent is connected to.

### `tableau_site_get`

Retrieve information about a Tableau site: name, content URL, storage quota, user quota, and status. Optionally include usage statistics.

Parameters:

- `include_usage_statistics` (`boolean`, optional): Set to `true` to include storage and user count statistics.

### `tableau_workbooks_list`

List published workbooks on a Tableau site. Supports filtering (e.g., `name:eq:SalesReport`, `ownerName:eq:jane`), sorting (`name:asc`, `updatedAt:desc`), and pagination.

Parameters:

- `filter` (`string`, optional): Filter expression, e.g. `name:eq:SalesReport` or `ownerName:eq:jane`.
- `sort` (`string`, optional): Sort expression, e.g. `name:asc` or `updatedAt:desc`.
- `page_number` (`integer`, optional): Page number (starts at 1).
- `page_size` (`integer`, optional): Items per page (max 1000).

### `tableau_workbook_search`

Search for workbooks on a Tableau site by exact name. Returns workbooks whose name matches the search term.

Parameters:

- `name` (`string`, required): The workbook name to search for (exact match).
- `page_number` (`integer`, optional): Page number (starts at 1).
- `page_size` (`integer`, optional): Items per page (max 1000).

### `tableau_workbook_get`

Retrieve detailed information about a specific workbook: name, owner, project, tags, views, and data connections. Optionally include view count statistics.

Parameters:

- `workbook_id` (`string`, required): Workbook LUID. Get it from `tableau_workbooks_list` → `workbooks.workbook[].id`.
- `include_usage_statistics` (`boolean`, optional): Set to `true` to include view count and high-water-mark statistics.

### `tableau_workbook_delete`

Permanently delete a workbook and all of its views from the Tableau site. This action cannot be undone.

Parameters:

- `workbook_id` (`string`, required): Workbook LUID. Get it from `tableau_workbooks_list`. WARNING: This is permanent.

### `tableau_workbook_connections_list`

List the data connections used by a workbook: connection type, server address, username, and whether the connection is embedded.

Parameters:

- `workbook_id` (`string`, required): Workbook LUID. Get it from `tableau_workbooks_list`.

### `tableau_views_list`

List all views (sheets and dashboards) across the entire site. Supports filtering, sorting, and pagination. Use `tableau_workbook_views_list` to scope to a single workbook.

Parameters:

- `filter` (`string`, optional): Filter expression, e.g. `name:eq:SalesDashboard`.
- `sort` (`string`, optional): Sort expression, e.g. `name:asc` or `viewCount:desc`.
- `include_usage_statistics` (`boolean`, optional): Set to `true` to include view count statistics.
- `page_number` (`integer`, optional): Page number (starts at 1).
- `page_size` (`integer`, optional): Items per page (max 1000).

### `tableau_workbook_views_list`

List all views (sheets and dashboards) within a specific workbook. Returns each view's LUID, name, content URL, and owner.

Parameters:

- `workbook_id` (`string`, required): Workbook LUID. Get it from `tableau_workbooks_list`.
- `include_usage_statistics` (`boolean`, optional): Set to `true` to include view count for each view.
- `filter` (`string`, optional): Filter expression, e.g. `name:eq:Overview`.
- `page_number` (`integer`, optional): Page number (starts at 1).
- `page_size` (`integer`, optional): Items per page.

### `tableau_view_get`

Retrieve detailed information about a specific view: name, owner, workbook, content URL, tags, and creation date.

Parameters:

- `view_id` (`string`, required): View LUID. Get it from `tableau_views_list` or `tableau_workbook_views_list` → `views.view[].id`.
- `include_usage_statistics` (`boolean`, optional): Set to `true` to include total view count.

### `tableau_datasources_list`

List published data sources on a Tableau site. Supports filtering (e.g., `name:eq:SalesData`, `type:eq:excel`), sorting, and pagination.

Parameters:

- `filter` (`string`, optional): Filter expression, e.g. `name:eq:SalesData` or `type:eq:excel`.
- `sort` (`string`, optional): Sort expression, e.g. `name:asc` or `updatedAt:desc`.
- `page_number` (`integer`, optional): Page number (starts at 1).
- `page_size` (`integer`, optional): Items per page (max 1000).

### `tableau_datasource_get`

Retrieve detailed information about a specific published data source: name, type, owner, project, tags, and connection details.

Parameters:

- `datasource_id` (`string`, required): Data source LUID. Get it from `tableau_datasources_list` → `datasources.datasource[].id`.

### `tableau_datasource_delete`

Permanently delete a published data source from the Tableau site. This action cannot be undone and will break any workbooks that depend on this data source.

Parameters:

- `datasource_id` (`string`, required): Data source LUID. Get it from `tableau_datasources_list`. WARNING: This is permanent.

### `tableau_projects_list`

List projects on a Tableau site. Projects organize workbooks and data sources. Supports filtering (e.g., `name:eq:Marketing`), sorting, and pagination.

Parameters:

- `filter` (`string`, optional): Filter expression, e.g. `name:eq:Marketing`.
- `sort` (`string`, optional): Sort expression, e.g. `name:asc`.
- `page_number` (`integer`, optional): Page number (starts at 1).
- `page_size` (`integer`, optional): Items per page (max 1000).

### `tableau_project_create`

Create a new project on a Tableau site. Optionally nest it under a parent project and set content permission behavior.

Parameters:

- `name` (`string`, required): Display name for the new project.
- `description` (`string`, optional): Optional description.
- `parent_project_id` (`string`, optional): Parent project LUID to create a sub-project. Get it from `tableau_projects_list`.
- `content_permissions` (`string`, optional): `ManagedByOwner` (default) or `LockedToProject`.

### `tableau_project_update`

Update a project's name, description, parent project, or content permission behavior.

Parameters:

- `project_id` (`string`, required): Project LUID. Get it from `tableau_projects_list` → `projects.project[].id`.
- `name` (`string`, optional): New display name.
- `description` (`string`, optional): New description.
- `parent_project_id` (`string`, optional): New parent project LUID to move the project.
- `content_permissions` (`string`, optional): `ManagedByOwner` or `LockedToProject`.

### `tableau_project_delete`

Permanently delete a project from the Tableau site. Content within the project is moved to the default project (not deleted). This action cannot be undone.

Parameters:

- `project_id` (`string`, required): Project LUID. Get it from `tableau_projects_list`. WARNING: This is permanent.

### `tableau_users_list`

List users on a Tableau site. Supports filtering (e.g., `siteRole:eq:SiteAdministratorCreator`), sorting (`name:asc`, `lastLogin:desc`), and pagination.

Parameters:

- `filter` (`string`, optional): Filter expression, e.g. `siteRole:eq:Viewer` or `name:eq:jane`.
- `sort` (`string`, optional): Sort expression, e.g. `name:asc` or `lastLogin:desc`.
- `page_number` (`integer`, optional): Page number (starts at 1).
- `page_size` (`integer`, optional): Items per page (max 1000).

### `tableau_user_get`

Retrieve information about a specific user: name, email, site role, last login, and authentication type.

Parameters:

- `user_id` (`string`, required): User LUID. Get it from `tableau_users_list` → `users.user[].id`.

### `tableau_user_add_to_site`

Add a user to the Tableau site with a specified role. If the user account does not exist, it is created. The `site_role` field controls what the user can do.

Parameters:

- `name` (`string`, required): Username or email address of the user to add.
- `site_role` (`string`, required): Role to assign: `SiteAdministratorCreator`, `SiteAdministratorExplorer`, `Creator`, `ExplorerCanPublish`, `Explorer`, `Viewer`, or `Unlicensed`.
- `auth_setting` (`string`, optional): Authentication type: `ServerDefault`, `SAML`, or `OpenIDConnect`.

### `tableau_user_remove_from_site`

Remove a user from the Tableau site. The user's content (workbooks, data sources) is transferred to the site admin. The user account itself is not deleted from the server.

Parameters:

- `user_id` (`string`, required): User LUID. Get it from `tableau_users_list`.

### `tableau_groups_list`

List groups on a Tableau site. Groups simplify permission management — you assign permissions once to a group and they apply to all members.

Parameters:

- `filter` (`string`, optional): Filter expression, e.g. `name:eq:Analytics`.
- `sort` (`string`, optional): Sort expression, e.g. `name:asc`.
- `page_number` (`integer`, optional): Page number (starts at 1).
- `page_size` (`integer`, optional): Items per page (max 1000).

### `tableau_group_create`

Create a new local group on a Tableau site. Optionally set a minimum site role for all group members.

Parameters:

- `name` (`string`, required): Display name for the new group.
- `minimum_site_role` (`string`, optional): Minimum site role for members: `Viewer`, `Explorer`, `Creator`, etc.

### `tableau_group_add_user`

Add a user to a group on a Tableau site. The user must already be a site member. Use this to manage group-based permissions.

Parameters:

- `group_id` (`string`, required): Group LUID. Get it from `tableau_groups_list` → `groups.group[].id`.
- `user_id` (`string`, required): User LUID. Get it from `tableau_users_list` → `users.user[].id`.

### `tableau_group_remove_user`

Remove a user from a group. The user remains a site member — only group membership is changed.

Parameters:

- `group_id` (`string`, required): Group LUID. Get it from `tableau_groups_list`.
- `user_id` (`string`, required): User LUID. Get it from `tableau_users_list`.

### `tableau_jobs_list`

List background jobs on a Tableau site. Jobs include extract refreshes, data source imports, and workbook publishes. Filter by status: `InProgress`, `Success`, `Failed`, or `Cancelled`.

Parameters:

- `filter` (`string`, optional): Filter expression, e.g. `status:eq:Failed`.
- `sort` (`string`, optional): Sort expression, e.g. `createdAt:desc`.
- `page_number` (`integer`, optional): Page number (starts at 1).
- `page_size` (`integer`, optional): Items per page (max 1000).

### `tableau_job_get`

Retrieve the current status and details of a background job: type, status (`InProgress`, `Success`, `Failed`, `Cancelled`), progress percentage, and error details if failed. Use this to poll after triggering a refresh.

Parameters:

- `job_id` (`string`, required): Job LUID returned from async operations like workbook or data source refreshes → `job.id`.

### `tableau_job_cancel`

Cancel a background job that is currently queued or in progress. Already completed, failed, or cancelled jobs cannot be cancelled.

Parameters:

- `job_id` (`string`, required): Job LUID. Get it from `tableau_jobs_list` or from a refresh response. Only queued/in-progress jobs can be cancelled.


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
