> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lightdash.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent identity

> Choose who AI agents run as in your warehouse, so its grants and policies decide what they can read

<Info>
  <Badge icon="flask" color="purple" size="sm" shape="pill">Beta</Badge> <Badge icon="building-plus" color="blue" size="sm" shape="pill">Enterprise</Badge> Agent identity is behind the `agent-identity` feature flag. On Lightdash Cloud, ask Lightdash support to turn it on for your organization. On a self-hosted instance, [enable the flag](/self-host/enterprise-features/ai-agents#agent-identity). Agent sign-in on Snowflake also needs the Snowflake agent integration. [What Beta means](/support/feature-maturity-levels).
</Info>

AI agents run SQL in your warehouse. By default, an agent query uses the same credentials as the person who asked. The warehouse cannot tell the agent from the person, so it applies the same rules to both.

Agent identity lets an organization admin choose a different identity for agent queries. The warehouse then sees agent queries as that identity, and your warehouse grants and policies decide what agents can read. Lightdash's own controls still apply on top. See [Data access control](/agents/data-access).

Agent identity is available on the warehouses that can enforce it: Snowflake, BigQuery, Databricks, Athena, Postgres, Redshift, Trino and ClickHouse.

| Warehouse | The person | The person's agent sign-in | The shared agent account |
| - | - | - | - |
| Snowflake | <Icon icon="check" /> | <Icon icon="check" /> | <Icon icon="check" /> |
| BigQuery | <Icon icon="check" /> | <Icon icon="x" className="matrix-no" /> | <Icon icon="check" /> |
| Databricks | <Icon icon="check" /> | <Icon icon="x" className="matrix-no" /> | <Icon icon="check" /> |
| Athena | <Icon icon="check" /> | <Icon icon="x" className="matrix-no" /> | <Icon icon="check" /> |
| Postgres | <Icon icon="check" /> | <Icon icon="x" className="matrix-no" /> | <Icon icon="check" /> |
| Redshift | <Icon icon="check" /> | <Icon icon="x" className="matrix-no" /> | <Icon icon="check" /> |
| Trino | <Icon icon="check" /> | <Icon icon="x" className="matrix-no" /> | <Icon icon="check" /> |
| ClickHouse | <Icon icon="check" /> | <Icon icon="x" className="matrix-no" /> | <Icon icon="check" /> |

What the warehouse sees with each identity:

* **The person:** the person. This is the default.
* **The person's agent sign-in:** the person, in a session that Snowflake verifies as an agent session.
* **The shared agent account:** one Snowflake user, BigQuery service account, Databricks service principal, AWS identity for Athena, Postgres login role, Redshift database user, Trino user or ClickHouse user that an admin adds to the project connection.

Lightdash also tags agent queries with the person they ran for and the agent that ran them, so you can find them in query history. On Snowflake, the tags are in `query_tag`. On BigQuery, they are job labels. Tags are not an identity: warehouse policies can't act on them. See [Find agent queries in the warehouse](#find-agent-queries-in-the-warehouse).

Jump to [the organization rule](#choose-who-agents-run-as), [Snowflake agent sign-in](#snowflake-agent-sign-in-for-each-person), [the Snowflake shared agent account](#snowflake-the-shared-agent-account), [the BigQuery shared agent account](#bigquery-the-shared-agent-account), [the Databricks shared agent account](#databricks-the-shared-agent-account), [the Athena shared agent account](#athena-the-shared-agent-account), [the Postgres shared agent account](#postgres-the-shared-agent-account), [the Redshift shared agent account](#redshift-the-shared-agent-account), [the Trino shared agent account](#trino-the-shared-agent-account), [the ClickHouse shared agent account](#clickhouse-the-shared-agent-account), [finding agent queries](#find-agent-queries-in-the-warehouse) or [what gets recorded](#what-gets-recorded).

## Choose who agents run as

Organization admins set one rule for each warehouse type. A rule applies to every connection of that type in every project.

A separate identity is always required. When you pick **The person's agent sign-in** or **A shared agent account**, Lightdash refuses every agent query that cannot run as that identity. It never falls back to the person's own credentials. Put the identity in place before you switch the rule:

* **Snowflake, agent sign-in:** set up the Snowflake agent integration. After you turn it on, Lightdash asks each person who uses agents to connect their agent. See [Set up the Snowflake agent integration](#set-up-the-snowflake-agent-integration) and [Connect your agent](#connect-your-agent).
* **Snowflake, shared agent account:** an admin adds a shared agent account on every Snowflake project connection. See [Set up the Snowflake shared agent account](#set-up-the-snowflake-shared-agent-account).
* **BigQuery:** an admin adds a shared agent account key on every BigQuery project connection. See [Set up the shared agent account](#set-up-the-bigquery-shared-agent-account).
* **Databricks:** an admin adds a shared agent account on every Databricks project connection. See [Set up the Databricks shared agent account](#set-up-the-databricks-shared-agent-account).
* **Athena:** an admin adds a shared agent account on every Athena project connection. See [Set up the Athena shared agent account](#set-up-the-athena-shared-agent-account).
* **Postgres:** an admin adds a shared agent account on every Postgres project connection. See [Set up the Postgres shared agent account](#set-up-the-postgres-shared-agent-account).
* **Redshift:** an admin adds a shared agent account on every Redshift project connection. See [Set up the Redshift shared agent account](#set-up-the-redshift-shared-agent-account).
* **Trino:** an admin adds a shared agent account on every Trino project connection. See [Set up the Trino shared agent account](#set-up-the-trino-shared-agent-account).
* **ClickHouse:** an admin adds a shared agent account on every ClickHouse project connection. See [Set up the ClickHouse shared agent account](#set-up-the-clickhouse-shared-agent-account).

<Steps>
  <Step title="Open Agents">
    Go to **Organization settings → Agents** and find the **Identity** section. It has one chip for each warehouse that your organization has a project on.
  </Step>

  <Step title="Choose the identity">
    Select the chip for your warehouse. A menu opens with the identities for that warehouse, each with one line that explains it. A tick marks the saved identity. Choose an identity.
  </Step>

  <Step title="Confirm the change">
    A confirmation opens and states the effect of the change. Select the confirm button to save. An "Agent identity saved." notification confirms it. **Cancel** closes the confirmation, and the rule does not change.
  </Step>
</Steps>

### Read the chips

<Frame>
  <img src="https://mintcdn.com/lightdash/KafA2i3NlUJIK4kg/images/agents/agent-identity/org-agent-identity-chips.png?fit=max&auto=format&n=KafA2i3NlUJIK4kg&q=85&s=c3ac524c3ea2e0a4b71c10ec830a0d69" alt="The Identity section with three chips: Snowflake with Agent sign-in and a green tick, BigQuery with Shared account and an orange 2, and Postgres with an icon only" width="2071" height="500" data-path="images/agents/agent-identity/org-agent-identity-chips.png" />
</Frame>

Each chip shows the warehouse, an icon for who agents run as, and the state of the setup:

| On the chip | What it means |
| - | - |
| An icon only | The rule is **The person**. |
| **Shared account** | The rule is **A shared agent account**. |
| **Agent sign-in** | The rule is **The person's agent sign-in**. Snowflake only. |
| A green tick | The setup is complete: every project on the warehouse has a shared agent account, or the Snowflake agent integration is verified. |
| An orange number | The number of projects on the warehouse that have no shared agent account. Agents are refused on them until a project admin adds one. Hold the pointer over the number, or open the chip, for a link to each project's **Agent identity** page. |
| An orange warning | The Snowflake agent integration needs attention. |

Hold the pointer over a chip for one sentence that says who agents run as on that warehouse.

<Frame>
  <img src="https://mintcdn.com/lightdash/KafA2i3NlUJIK4kg/images/agents/agent-identity/org-agent-identity-chip-menu.png?fit=max&auto=format&n=KafA2i3NlUJIK4kg&q=85&s=8568be60e6ccd69ea507d1a65c8ccb96" alt="The BigQuery chip's menu: the warning 2 projects have no shared agent account, Agents are refused on them until a project admin adds one, links to the Finance and Marketing projects, and the choices The person and A shared agent account under BigQuery agents run as, with a tick on A shared agent account" width="2071" height="1092" data-path="images/agents/agent-identity/org-agent-identity-chip-menu.png" />
</Frame>

A warehouse with no project shows no chip while its rule is **The person**. When the organization has no project, the section says "No warehouses in use yet."

Nothing saves until you confirm:

* **To A shared agent account.** The confirmation lists the projects on that warehouse that have no shared agent account yet, with a link to each project's **Agent identity** page. Agents stop working on these projects until a project admin adds one.
* **Back to The person.** The confirmation states that agents get the same warehouse access as the person who asks.
* **To The person's agent sign-in.** No confirmation opens and nothing saves. The setup steps open under the chips. The rule changes when you select **Turn on**. See [Set up the Snowflake agent integration](#set-up-the-snowflake-agent-integration).

<Frame>
  <img src="https://mintcdn.com/lightdash/Yds9mlTfEmGhzoxo/images/agents/agent-identity/org-agent-identity-confirm-ai-service-account.png?fit=max&auto=format&n=Yds9mlTfEmGhzoxo&q=85&s=489e1e5d35cc0738783e7458bfcd93d4" alt="The confirmation Use the shared agent account for BigQuery?, with the note 2 projects have no shared agent account yet: Finance, Marketing. Agents stop working on them until a project admin adds one, and Cancel and Use the shared agent account buttons" width="716" height="386" data-path="images/agents/agent-identity/org-agent-identity-confirm-ai-service-account.png" />
</Frame>

<Frame>
  <img src="https://mintcdn.com/lightdash/Yds9mlTfEmGhzoxo/images/agents/agent-identity/org-agent-identity-confirm-same-credentials.png?fit=max&auto=format&n=Yds9mlTfEmGhzoxo&q=85&s=66347a647c4a9d8e56510520d37ac040" alt="The confirmation Use each person's credentials for BigQuery?, which says agents will get the same BigQuery access as the person asking, with Cancel and Use each person's credentials buttons" width="716" height="296" data-path="images/agents/agent-identity/org-agent-identity-confirm-same-credentials.png" />
</Frame>

With **The person**, Lightdash never refuses an agent query for a missing identity.

Changing the rule does not create warehouse grants or policies. Set those up in your warehouse for the identity you choose.

### When an identity changes

Lightdash checks the identity on every agent query. A change applies from the next agent query:

* **An admin changes the rule.** The next agent query runs as the new identity.
* **A person disconnects their agent sign-in.** Lightdash refuses their next agent query on Snowflake until they connect again.
* **Snowflake revokes an agent sign-in.** Lightdash refuses the next agent query, and the person connects again.
* **An admin removes the shared agent account key.** Lightdash refuses the next agent query on that project.
* **An admin replaces the shared agent account key.** The next agent query runs with the new key.
* **An admin deactivates a person.** Lightdash deletes that person's Snowflake agent sign-in. See [When you take agent access away](/agents/agent-permissions#when-you-take-agent-access-away).

Results follow the identity too. Lightdash does not serve results that an agent produced as an identity that is no longer current. This covers results that an agent already showed, results that a later query reuses, and cached results. A person's own queries never get cached results that an agent identity produced. A new query runs as the current identity.

One exception: when you switch the BigQuery rule away from **A shared agent account** and back, and the key did not change, the results that the key produced become available again. Replace the key to stop this.

### Who can read a saved result

While agent identity is on, Lightdash checks every read of a saved or cached query result. Both must be true:

* The reader still has access to the project, the content and the fields.
* The identity that produced the result matches the reader.

So:

* A person cannot read results that a shared agent account, an agent sign-in, or another person's own credentials produced.
* An agent reads only results that its own agent produced.
* When a person's user attributes or field access rules change, they cannot read their older results.
* Results saved before agent identity was turned on cannot be read.

These reads are refused with: "These results are not compatible with your current access. Run the query again." A new query runs as the current identity and with the current access.

This check does not cover scheduled delivery attachments or signed download links yet.

### When the shared agent account is missing or fails

While the rule for a warehouse is **A shared agent account**, Lightdash refuses agent queries on a project that has no working shared agent account. It never falls back to the connection's own credentials. The refusals are:

* **No shared agent account:** "Agents can't run on this project yet. It needs a shared agent account."
* **The shared agent account failed to sign in:** "Agents can't query Jaffle shop right now. Its shared agent account failed to sign in. A project admin can check it in Agent identity."

Each surface points to the project's **Agent identity** page, where the fix is:

* **In the app,** the chat shows the refusal. People who can manage the project also see an **Open project agent settings** link.
* **In Slack,** the agent posts only the refusal message with an **Open project agent settings** button, and the task card says "Needs an admin".
* **In MCP,** the tool result shows the refusal message.

### The Lightdash CLI

While agent identity is on, the Lightdash CLI runs as the person. CLI queries use the person's own warehouse access. Lightdash does not mark them as agent queries, and [agent permissions](/agents/agent-permissions) do not limit them. `lightdash agent status` and `lightdash agent connect` say "The CLI runs as you until agent connect issues an agent credential."

On a project where agents use a shared agent account, both commands print "Not needed: agents on this project use the shared agent account" and exit with code 0. See [`lightdash agent status`](/workflow/cli/reference#lightdash-agent-status).

## Find agent queries in the warehouse

Lightdash records two identities on every agent query:

* **Who the agent ran for:** the Lightdash user, or the Lightdash service account.
* **Which agent ran it:** the surface and the client.

Lightdash keeps both in its query history, and sends them to the warehouse as tags. Use the tags to audit agent activity next to the warehouse's own record of who ran the query.

| Tag | Value |
| - | - |
| `agent` | `true` on every agent query. |
| `user_uuid` | The UUID of the Lightdash user that triggered the query, which is the person the agent ran for. |
| `agent_surface` | Where the agent ran. See the table below. |
| `agent_client` | The client of that agent. See the table below. |

| `agent_surface` | `agent_client` |
| - | - |
| `in_app_agent` | `lightdash-chat` |
| `mcp` | The OAuth client ID of the MCP client. `unknown` when the client authenticates with a personal access token or a service account token. |
| `slack_agent` | The Slack app ID. |
| `data_app` | `lightdash-data-app`, for data app sample queries. |
| `ai_summary` | `lightdash-ai-summary`, for AI summaries in scheduled deliveries. |

`agent_client` is the client ID in lower case. When the ID has characters that a label cannot hold, or is longer than 60 characters, the value is `h-` followed by a 32-character hash. The same ID always gives the same hash.

On BigQuery, every agent job has the `agent`, `agent_surface` and `agent_client` labels. They come before the other labels, so the 64-label limit does not drop them. On Snowflake, the same keys come first in the JSON `query_tag`, so the 2,000-character limit does not cut them.

<CodeGroup>
  ```sql BigQuery theme={null}
  SELECT creation_time, user_email, job_id,
         (SELECT value FROM UNNEST(labels) WHERE key = 'user_uuid') AS lightdash_user_uuid,
         (SELECT value FROM UNNEST(labels) WHERE key = 'agent_surface') AS agent_surface,
         (SELECT value FROM UNNEST(labels) WHERE key = 'agent_client') AS agent_client
  FROM `region-us`.INFORMATION_SCHEMA.JOBS
  WHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
    AND EXISTS (
      SELECT 1 FROM UNNEST(labels) AS label
      WHERE label.key = 'agent_surface' AND label.value = 'mcp'
    )
  ORDER BY creation_time DESC;
  ```

  ```sql Snowflake theme={null}
  SELECT start_time, user_name, agent_type,
         TRY_PARSE_JSON(query_tag):user_uuid::STRING AS lightdash_user_uuid,
         TRY_PARSE_JSON(query_tag):agent_surface::STRING AS agent_surface,
         TRY_PARSE_JSON(query_tag):agent_client::STRING AS agent_client
  FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
  WHERE start_time >= DATEADD('day', -1, CURRENT_TIMESTAMP())
    AND TRY_PARSE_JSON(query_tag):agent_surface::STRING = 'mcp'
  ORDER BY start_time DESC;
  ```
</CodeGroup>

Each example lists the agent queries from MCP in the last day. Change the `agent_surface` filter, or filter on `agent_client` to find one client, for example one Slack app. On BigQuery, replace `region-us` with the region of your datasets. For the account each query ran as, read `user_email` on BigQuery and `user_name` on Snowflake. Other Lightdash query tags are listed in [Query tags](/workspace-admin/usage-analytics#query-tags).

Agent queries skip the warehouse's own result cache on BigQuery, Snowflake, Databricks, Redshift and ClickHouse. Each agent query runs in the warehouse with the current identity, and the warehouse applies its policies at that time.

## What gets recorded

When an agent changes content, Lightdash records that an agent made the change and for which person. The record has the surface (in-app agent, MCP or Slack agent), the client and the agent. It comes only from the signed-in credential and the agent runtime. Request headers and tool arguments cannot set it.

* **Content with versions.** New versions of charts, dashboards, SQL charts, documents and data apps record the agent. Chart and dashboard version history show "Changed by an agent for" followed by the person and the surface. When you restore a version, the new version does not copy the agent of the restored version.
* **Other writes and refusals.** Agent writes that make no version, and agent refusals, go to the agent action log. Examples are spaces, scheduled deliveries, content verification, access changes, share links and dbt write-back commits. Each entry has the organization, project, time, agent, object, action, outcome (allowed or denied), the policy that decided and a reason code. It never has data, tool arguments, SQL, recipients, URLs or secrets.

<Frame>
  <img src="https://mintcdn.com/lightdash/pnJsr39POUeS6PgH/images/agents/agent-identity/chart-version-history-agent.png?fit=max&auto=format&n=pnJsr39POUeS6PgH&q=85&s=8ffe243c222071a8c581405fb270c9e0" alt="Chart version history with two versions marked Changed by an agent for David Attenborough, one from the In-app agent and one from MCP, and an older version Updated by David Attenborough" width="484" height="348" data-path="images/agents/agent-identity/chart-version-history-agent.png" />
</Frame>

Each agent action log entry also goes to the application audit log. See [Agent identity audit entries and logs](/self-host/enterprise-features/ai-agents#agent-identity-audit-entries-and-logs). The query history cleanup job deletes old entries, with the same retention as query history (`QUERY_HISTORY_RETENTION_DAYS`).

## Snowflake: agent sign-in for each person

With this identity, each person connects their agent through a separate Snowflake OAuth sign-in. Snowflake then knows that the session is an agent session. Your row access policies, masking policies, session policies and views can act on that.

The result: Snowflake, not Lightdash, enforces what agents may read. A person can see a value in a dashboard while their agent sees `REDACTED`.

### Who sees what

| Signal | Where it appears | What it tells you |
| - | - | - |
| Agent tag | `query_tag` in Snowflake query history | Lightdash ran the query for an agent. |
| Agent session | `SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED')` inside the session | Snowflake verified the session as an agent session. Policies read this value. |
| Agent type | `agent_type = 'EXTERNAL_AGENT'` in `SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY` | Snowflake ran the query in a verified agent session. |

A query tag alone does not prove that Snowflake verified the session. The agent session value and `agent_type` do.

### Before you start

* **The Snowflake agent integration is set up for your organization.** See [Set up the Snowflake agent integration](#set-up-the-snowflake-agent-integration). Until it is set up, the option **The person's agent sign-in** shows "Needs a one-time Snowflake setup".
* **Your project uses the same Snowflake account as the integration.** An organization has one agent integration for one Snowflake account. A project on a second Snowflake account cannot complete the agent connection.
* **Each person has an allowed default role.** Snowflake blocks the `ACCOUNTADMIN`, `SECURITYADMIN`, `ORGADMIN` and `GLOBALORGADMIN` roles from OAuth sessions. The agent sign-in uses the person's default role. Each person who uses agents needs a default role outside that list.

The agent sign-in is separate from the person's normal [Sign in with Snowflake](/integrations/connect-project#sign-in-with-snowflake) connection. A person can have both.

### Set up the Snowflake agent integration

An organization admin sets up the integration once, from **Organization settings → Agents**. The setup shows only after you choose **The person's agent sign-in** in the Snowflake row. The rule does not change until you select **Turn on**, so agents keep running as each person during the setup.

<Steps>
  <Step title="Choose agent sign-in">
    Select the Snowflake chip and choose **The person's agent sign-in**. In the menu, this choice says "Setup needed" until the integration is set up. The **Set up the Snowflake agent integration** steps open under the chips, with a "Not active yet" badge. The rule does not change yet.
  </Step>

  <Step title="Copy and run in Snowflake">
    Copy the `CREATE SECURITY INTEGRATION` SQL and run it in Snowflake as `ACCOUNTADMIN`. The SQL has the redirect URL of your instance. The second statement returns the client ID and the client secret.
  </Step>

  <Step title="Add your Snowflake account and client details">
    Enter the **Snowflake account URL**, for example `https://myorg-myaccount.snowflakecomputing.com`. Paste the `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` values from the query in step 1 into **Client ID** and **Client secret**. Select **Save**. A "Snowflake client saved." notification confirms it. Lightdash works out the sign-in and token addresses from the account URL.
  </Step>

  <Step title="Verify">
    Select **Verify integration**. Lightdash runs the checks below and names each missing piece.
  </Step>

  <Step title="Turn on">
    Select **Turn on**. It stays disabled until the required checks pass. **Cancel** closes the steps and keeps the saved rule.
  </Step>
</Steps>

<Frame>
  <img src="https://mintcdn.com/lightdash/Yds9mlTfEmGhzoxo/images/agents/agent-identity/org-agent-identity-snowflake-setup.png?fit=max&auto=format&n=Yds9mlTfEmGhzoxo&q=85&s=c0baa28aac23657d30488e09faef261b" alt="The Set up the Snowflake agent integration steps with a Not active yet badge: the CREATE SECURITY INTEGRATION SQL with a copy button, the Add your Snowflake account and client details form with the placeholders OAUTH_CLIENT_ID from the query above and OAUTH_CLIENT_SECRET from the query above, and Verify with OAuth client settings Failed: Not saved, Authorization endpoint Not checked and Agent session Not checked" width="974" height="1108" data-path="images/agents/agent-identity/org-agent-identity-snowflake-setup.png" />
</Frame>

Lightdash stores the client for your organization and encrypts the secret. It never shows the secret again. Only organization admins can save or replace the client. After you save, step 2 shows the account URL, the client ID, "Client secret saved" and a **Replace client** button.

<Frame>
  <img src="https://mintcdn.com/lightdash/Yds9mlTfEmGhzoxo/images/agents/agent-identity/org-agent-identity-snowflake-client-saved.png?fit=max&auto=format&n=Yds9mlTfEmGhzoxo&q=85&s=dbd078de7f5ce171d9f50beb8ed70bf2" alt="Step 2 Add your Snowflake account and client details after a save: the Snowflake account URL (hidden), the client ID, Client secret saved and a Replace client button" width="944" height="348" data-path="images/agents/agent-identity/org-agent-identity-snowflake-client-saved.png" />
</Frame>

When you replace the client, the agent sign-ins that people made with the old client stop working. Each person connects their agent again.

| Check | Required for **Turn on** | What it checks |
| - | - | - |
| OAuth client settings | Yes | A client is saved for your organization. With no client, the result is "Not saved. Paste the client ID and secret from Snowflake in the form above, then verify again." It never shows the client secret. |
| Authorization endpoint | Yes | Snowflake's sign-in page answers. A pass says "Snowflake's sign-in page responded." With no client, the result is "Save the OAuth client first." A pass does not prove that the integration settings are correct. When the endpoint returns HTTP 404 or 405, check the Snowflake account URL. |
| Agent session | No | At least one person in the organization has connected their agent. Lightdash saves an agent connection only when Snowflake marks the session as an agent session. Until then, the result links to **My agent identity**. |

<Frame>
  <img src="https://mintcdn.com/lightdash/Yds9mlTfEmGhzoxo/images/agents/agent-identity/org-agent-identity-snowflake-verify-passed.png?fit=max&auto=format&n=Yds9mlTfEmGhzoxo&q=85&s=d68a6c7c47bb0b55cdce006d5eac4dce" alt="Verify results: OAuth client settings Passed, Using the client saved for this organisation; Authorization endpoint Passed, Snowflake's sign-in page responded; Agent session Not checked, with a My agent identity link" width="944" height="368" data-path="images/agents/agent-identity/org-agent-identity-snowflake-verify-passed.png" />
</Frame>

Lightdash cannot sign in to Snowflake for you, so Verify checks only what the server can reach. The agent session check passes after the first person connects.

When the integration is already set up, choosing **The person's agent sign-in** skips the steps and shows **Turn on agent sign-in** with **Turn on** and **Cancel**.

After you turn it on, the setup closes and the Snowflake chip shows a green tick. To see the setup again, select the Snowflake chip, then **View setup**. The setup opens under the chips with the line "Verified · Snowflake agent integration · checked" followed by the time. **Hide setup** closes it. When a later check fails, the chip shows an orange warning and the setup opens by itself.

<Frame>
  <img src="https://mintcdn.com/lightdash/KafA2i3NlUJIK4kg/images/agents/agent-identity/org-agent-identity-snowflake-view-setup.png?fit=max&auto=format&n=KafA2i3NlUJIK4kg&q=85&s=68998561efada58357de5d398facf12a" alt="The Identity section with the Snowflake setup open under the chips: the line Verified · Snowflake agent integration · checked less than a minute ago, a Hide setup button, and the three setup steps each with a green tick" width="2071" height="1590" data-path="images/agents/agent-identity/org-agent-identity-snowflake-view-setup.png" />
</Frame>

Lightdash does not save **The person's agent sign-in** while no OAuth client is configured. This keeps an admin from saving a rule that refuses every agent query.

### Connect your agent

Each person who uses agents connects their own agent once, after an admin turns on agent sign-in.

<Steps>
  <Step title="Open My agent identity">
    Go to **Settings → My agent identity** and find the **Snowflake** card.
  </Step>

  <Step title="Select Connect agent">
    Select **Connect agent**. A Snowflake sign-in opens in a popup.
  </Step>

  <Step title="Approve the sign-in">
    Sign in to Snowflake and approve the request. Lightdash checks that Snowflake marks the session as an agent session before it saves the connection.
  </Step>
</Steps>

The card then shows **Connected**, a **Disconnect** button and "Your agent sign-in lasts until" followed by the date, for example "7 Jan 2027".

<Frame>
  <img src="https://mintcdn.com/lightdash/18J8lppse3Q52tRw/images/agents/agent-identity/my-agent-connections-connected.png?fit=max&auto=format&n=18J8lppse3Q52tRw&q=85&s=c9c946e9e0f746881c25066619133caa" alt="My agent identity with a Snowflake card marked Connected, the line Your agent sign-in lasts until 7 Jan 2027 and a Disconnect button, and a Databricks card that says agents run as the shared agent account your admin set up" width="1016" height="404" data-path="images/agents/agent-identity/my-agent-connections-connected.png" />
</Frame>

To remove the agent connection, select **Disconnect**. While the Snowflake rule is **The person's agent sign-in**, AI features lock again for that person until they reconnect.

This connection is separate from your [personal warehouse connections](/personal-settings/personal-warehouse-connections). Dashboards and the SQL Runner keep using your normal credentials.

### My agent identity

**Settings → My agent identity** shows when the organization's rule for at least one warehouse is not **The person**. The page has one card for each warehouse where the person has a project and the rule needs one:

* **The person's agent sign-in** (Snowflake): a card with the person's sign-in status and a button.
* **A shared agent account** (Snowflake, BigQuery, Databricks, Athena, Postgres, Redshift, Trino or ClickHouse): a card that says, for example, "Agents on Databricks projects run as the shared agent account your admin set up. They read only what that account can read. Your own access doesn't change." There is nothing to do.

When no card shows, the page says "Agents use your own warehouse access. There's nothing to connect."

<Frame>
  <img src="https://mintcdn.com/lightdash/18J8lppse3Q52tRw/images/agents/agent-identity/my-agent-connections-ai-service-account.png?fit=max&auto=format&n=18J8lppse3Q52tRw&q=85&s=18aedc6e737e802ca9f3448be82ff413" alt="My agent identity with Snowflake, BigQuery and Databricks cards, each saying agents on that warehouse's projects run as the shared agent account your admin set up, read only what that account can read, and your own access doesn't change" width="1016" height="548" data-path="images/agents/agent-identity/my-agent-connections-ai-service-account.png" />
</Frame>

The Snowflake sign-in card shows one of these states:

| State | What it means | Button |
| - | - | - |
| **Not connected** | The person has not connected their agent. Their AI questions on Snowflake projects are refused. | **Connect agent** |
| **Connected** | The agent sign-in works. The card says "Your agent sign-in lasts until" followed by the date. | **Disconnect** |
| **Expired** | The agent sign-in has ended. The card says when it ended, for example "Your Snowflake agent sign-in ended on 7 Oct 2026. Sign in again to keep using agents on Snowflake projects." | **Sign in again** |
| **Failing** | The last sign-in from this page failed. The card shows the error. | **Connect agent** |
| **Not available** | The Snowflake agent integration is not set up. The card says "Agent sign-in is not set up yet. Ask an admin to finish the Snowflake setup." | None |

When the browser blocks the sign-in window, the card says "Your browser blocked the Snowflake sign-in window. Allow pop-ups for this site, then try again." The state does not change.

<Frame>
  <img src="https://mintcdn.com/lightdash/18J8lppse3Q52tRw/images/agents/agent-identity/my-agent-connections-expired.png?fit=max&auto=format&n=18J8lppse3Q52tRw&q=85&s=186a46dd002fa5c407341ff83d31feb3" alt="My agent identity with a Snowflake card marked Expired, the line Your Snowflake agent sign-in ended on 7 Oct 2026, and a Sign in again button, and a Databricks card for the shared agent account" width="1016" height="404" data-path="images/agents/agent-identity/my-agent-connections-expired.png" />
</Frame>

### How long the agent connection lasts

Lightdash refreshes the agent sign-in in the background while the Snowflake grant lasts, so people do not sign in again for each session. The length of the grant comes from `OAUTH_REFRESH_TOKEN_VALIDITY` on the Snowflake security integration. **Settings → My agent identity** shows "Your agent sign-in lasts until" followed by the date.

Snowflake decides when the connection ends:

* **The grant ended or Snowflake revoked it.** Snowflake returns `invalid_grant`. Lightdash refuses the agent query with "Your agent connection expired. Connect again." The person connects again.
* **Snowflake is not available for a short time.** For example, a network error, a timeout, a rate limit or a server error. The agent query fails with an error that you can retry. The agent connection stays in place.
* **Snowflake rejects the Lightdash OAuth client.** Lightdash refuses the agent query with "The warehouse OAuth client was rejected. Ask an administrator to check the agent sign-in settings." Connecting again does not help. An admin checks the security integration in Snowflake and the client on **Organization settings → Agents**. See [Set up the Snowflake agent integration](#set-up-the-snowflake-agent-integration).

### Set the Snowflake rule

An organization admin selects **Turn on** for **The person's agent sign-in** on **Organization settings → Agents**. See [Set up the Snowflake agent integration](#set-up-the-snowflake-agent-integration). Tell people before you turn it on: until each person connects, Lightdash refuses their agent queries on Snowflake.

While the rule is set:

* Every AI query on a Snowflake connection must run through the person's agent connection. Lightdash refuses queries from people who have not connected.
* This covers chat, [MCP](/agents/lightdash-mcp), [Slack](/integrations/slack) and data app sample queries.
* Every Snowflake connection form shows "Agent identity required by your organisation", with a link to the organization settings.

<Frame>
  <img src="https://mintcdn.com/lightdash/LRLOrRwsbA2U0H2D/images/agents/agent-identity/snowflake-connection-requirement.png?fit=max&auto=format&n=LRLOrRwsbA2U0H2D&q=85&s=5d66e91f63fe9a3f40f4448b9bfc6b92" alt="The Type field of a Snowflake connection form with the read-only note Agent identity required by your organisation and an Organisation settings link" width="1454" height="525" data-path="images/agents/agent-identity/snowflake-connection-requirement.png" />
</Frame>

The rule does not create Snowflake policies. Without policies, agent sessions read the same data as the person. See [Write Snowflake policies for agent sessions](#write-snowflake-policies-for-agent-sessions).

Changing the Snowflake row back to **The person** does not revoke Snowflake tokens that people already issued.

### Until a person connects

While the Snowflake rule is **The person's agent sign-in** and a person has not connected their agent, Lightdash refuses their AI queries on Snowflake projects. The refusal message is "Connect your agent to the warehouse once so it can run as you." When the agent connection has expired, the message is "Your agent connection expired. Connect again."

Each surface shows the person how to connect:

* **In the app,** AI composers are hidden for Snowflake projects. This includes the Ask AI launcher, the homepage ask box and AI help in other parts of the app. The agent chat shows a **Connect your agent to your warehouse** card with a **Connect agent** button and a **Manage agent connections** link.
* **In Slack,** the agent posts only the refusal: "I need you to sign in to Snowflake before I can run this. It takes about 30 seconds. Then ask me again." It has a **Connect agent** button, and the task card says "Needs your sign-in".
* **In MCP,** the tool result shows the refusal message and a link to connect, and tells the agent to run the same call again after the person connects. The `connect_agent` tool returns the person's connection status and the same link, so an MCP client can offer it before the first query.
* **In the CLI,** `lightdash agent status` shows the refusal message and the link. `lightdash agent connect` prints the link, opens it in the browser and counts down while it waits for the person to approve. When the person approves, it shows who agents now run as and until when, and asks them to run their last command again. See [`lightdash agent connect`](/workflow/cli/reference#lightdash-agent-connect).

Every **Connect agent** button and link opens the same Snowflake sign-in as **Connect agent** in **Settings → My agent identity**.

When a refusal needs an admin instead, for example because the last check of the agent connection failed, Slack posts only the refusal message with an **Open agent settings** button, and the task card says "Needs an admin". The button opens **Organization settings → Agents**.

<Frame>
  <img src="https://mintcdn.com/lightdash/LRLOrRwsbA2U0H2D/images/agents/agent-identity/connect-agent-chat-card.png?fit=max&auto=format&n=LRLOrRwsbA2U0H2D&q=85&s=3e69999ffd94426b0295d268a8e7f889" alt="The Connect your agent to your warehouse card with a Connect agent button" width="2208" height="529" data-path="images/agents/agent-identity/connect-agent-chat-card.png" />
</Frame>

### Write Snowflake policies for agent sessions

Snowflake owns the data rules. Your policies decide what agent sessions may read.

<Warning>
  Lightdash does not create these policies. Adapt the examples to your data and existing policies before you attach them. Use a Snowflake role with the required policy privileges.
</Warning>

Each example reads `SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED')`. The value is true only in a verified agent session.

#### Hide rows from agents

A row access policy can exclude sensitive rows from agent sessions:

```sql theme={null}
CREATE ROW ACCESS POLICY agent_rows
  AS (is_sensitive BOOLEAN) RETURNS BOOLEAN ->
    NOT COALESCE(SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED')::BOOLEAN, FALSE)
    OR NOT is_sensitive;
ALTER TABLE protected_records
  ADD ROW ACCESS POLICY agent_rows ON (is_sensitive);
```

#### Mask values from agents

A masking policy can hide a value from agent sessions:

```sql theme={null}
CREATE MASKING POLICY agent_mask
  AS (value STRING) RETURNS STRING ->
    CASE WHEN SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED')::BOOLEAN
      THEN 'REDACTED'
      ELSE value
    END;
ALTER TABLE protected_records MODIFY COLUMN email
  SET MASKING POLICY agent_mask;
```

#### Use a view on Snowflake Standard Edition

Row access and masking policies need Snowflake Enterprise Edition. On Standard Edition, a view gives the same result for the columns it covers:

```sql theme={null}
CREATE OR REPLACE VIEW customers_v AS
SELECT customer_id, name,
       CASE WHEN SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED')::BOOLEAN
            THEN 'REDACTED' ELSE phone END AS phone
FROM customers;
```

Point your dbt model at the view, not the table.

#### Limit what an agent session can do

A session policy can limit the privileges of an agent session:

```sql theme={null}
CREATE SESSION POLICY agent_session_scope
  AGENT_RESTRICTED_SESSION_SCOPE = 'SNOWFLAKE$DATA_READ_WITH_AI';
ALTER USER example_user SET SESSION POLICY agent_session_scope;
```

The restricted scope limits the privileges the role already has. It does not grant new privileges. Review any existing session policy before you replace it.

For the full Snowflake reference, see [agent identity](https://docs.snowflake.com/en/user-guide/agent-identity) and [restricted session scopes](https://docs.snowflake.com/en/user-guide/restricted-session-scope).

### Verify on Snowflake

Run the same check in an agent session and in a normal session for the same person. For the agent session, ask an agent to run the SQL, for example in [SQL mode](/agents/use-ai-agents#sql-mode) or through the MCP `run_sql` tool. For the normal session, run it in a Snowflake worksheet as that person.

```sql theme={null}
SELECT SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED') AS agent_activated,
       SYS_CONTEXT('SNOWFLAKE$SESSION', 'ACTIVE_RESTRICTED_SESSION_SCOPES') AS active_scopes;
```

`agent_activated` is true only in the agent session.

Then inspect recent query history with a role that can read account usage:

```sql theme={null}
SELECT query_id, start_time, user_name, role_name, agent_type, query_tag
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE start_time >= DATEADD('hour', -1, CURRENT_TIMESTAMP())
ORDER BY start_time DESC;
```

The agent query has `agent_type = 'EXTERNAL_AGENT'` and the Lightdash agent tag in `query_tag`. `QUERY_HISTORY` has an ingestion delay, so new queries can take a while to appear. See [QUERY\_HISTORY](https://docs.snowflake.com/en/sql-reference/account-usage/query_history).

Finally, query a protected table through an agent and as the person. Check that your row access, masking and session policies give the restrictions you expect.

## Snowflake: the shared agent account

With this identity, agent queries on a Snowflake project run as one Snowflake user that signs in with a key pair. An admin adds the user, its private key, a role and a warehouse to the project connection. Snowflake then applies that role's grants and your policies to every agent query.

<Warning>
  Every person's agent gets the same access: the grants of the shared agent account's role. Snowflake does not apply each person's own role to agent queries. Grant the role only the data that every agent user may see.
</Warning>

Use this identity when every agent user may see the same data. To let Snowflake apply each person's own access to their agent, use [the person's agent sign-in](#snowflake-agent-sign-in-for-each-person) instead.

The shared agent account replaces only the identity of the connection: the user, key, role and warehouse. The account, database and schema stay from the connection. The connection's custom query tag does not carry over. Lightdash still tags each agent query with the person it ran for. See [Find agent queries in the warehouse](#find-agent-queries-in-the-warehouse).

### Set up the Snowflake shared agent account

While the project has no shared agent account, the **Shared agent account** card on **Project settings → Agent identity** shows three setup steps: create the account in Snowflake, grant it only the data agents may read, and add it here and select **Test**. Each step has its SQL and a copy button.

<Steps>
  <Step title="Create a key pair">
    Create an RSA key pair for the user. See [Key-pair authentication](https://docs.snowflake.com/en/user-guide/key-pair-auth).
  </Step>

  <Step title="Create the account in Snowflake">
    Create a separate Snowflake user and role for agents. The role and warehouse must exist:

    ```sql theme={null}
    CREATE USER AI_AGENT
      TYPE = SERVICE_AGENT
      RSA_PUBLIC_KEY = '<public key without PEM headers>'
      DEFAULT_ROLE = AI_AGENT_ROLE
      DEFAULT_WAREHOUSE = AI_AGENT_WH;
    GRANT ROLE AI_AGENT_ROLE TO USER AI_AGENT;
    ```

    `TYPE = SERVICE_AGENT` is recommended, not required. With it, Snowflake marks every session of the user as an agent session.
  </Step>

  <Step title="Grant it only the data agents may read">
    Grant the role `USAGE` on the warehouse, database and schema, and `SELECT` on each table that agents may read:

    ```sql theme={null}
    GRANT USAGE ON WAREHOUSE AI_AGENT_WH TO ROLE AI_AGENT_ROLE;
    GRANT USAGE ON DATABASE <database> TO ROLE AI_AGENT_ROLE;
    GRANT USAGE ON SCHEMA <database>.<schema> TO ROLE AI_AGENT_ROLE;
    GRANT SELECT ON TABLE <database>.<schema>.<table> TO ROLE AI_AGENT_ROLE;
    ```
  </Step>

  <Step title="Add the shared agent account">
    Select **Add shared agent account**. Enter the **User**, upload or paste the **Private key**, and enter the **Passphrase** if the key has one, the **Role** and the **Warehouse**. Select **Test and save**. Lightdash signs in with the key pair before it saves. **Test** checks the values without saving them.
  </Step>

  <Step title="Set the Snowflake rule">
    Add a shared agent account on every Snowflake project connection first. Then an organization admin selects the Snowflake chip on **Organization settings → Agents** and chooses **A shared agent account**. See [Choose who agents run as](#choose-who-agents-run-as).
  </Step>
</Steps>

<Frame>
  <img src="https://mintcdn.com/lightdash/18J8lppse3Q52tRw/images/agents/agent-identity/snowflake-ai-service-account-setup.png?fit=max&auto=format&n=18J8lppse3Q52tRw&q=85&s=489a2d0823554235ae43fd221400f7c8" alt="The shared agent account card on a Snowflake project with no account: the alert Agents are refused on this project until you add a shared agent account, an Add shared agent account button, and the setup steps with the CREATE USER SQL, the GRANT statements and Add it here and select Test" width="1992" height="1584" data-path="images/agents/agent-identity/snowflake-ai-service-account-setup.png" />
</Frame>

<Frame>
  <img src="https://mintcdn.com/lightdash/aFaeNoL9dy2TdEbL/images/agents/agent-identity/snowflake-ai-service-account-form.png?fit=max&auto=format&n=aFaeNoL9dy2TdEbL&q=85&s=f3ecda51dd0a63336c5bc6f79c478a7e" alt="The shared agent account form for Snowflake with User, Private key with Upload file and Paste key options and the key blurred, Passphrase (optional), Role and Warehouse fields, the line Signs in as AI_AGENT · AI_AGENT_ROLE, and Cancel, Test and Test and save buttons" width="716" height="688" data-path="images/agents/agent-identity/snowflake-ai-service-account-form.png" />
</Frame>

The Snowflake chip on **Organization settings → Agents** shows the number of projects that have no shared agent account. See [Read the chips](#read-the-chips).

### Agent sessions and your policies

With `TYPE = SERVICE_AGENT`, `SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED')` is true in every session of the shared agent account. The row access, masking and session policies in [Write Snowflake policies for agent sessions](#write-snowflake-policies-for-agent-sessions) then apply to agent queries. With another user type, Snowflake does not mark the sessions, and only the role's grants restrict agents. See [SERVICE\_AGENT users](https://docs.snowflake.com/en/sql-reference/sql/create-user).

### Test, replace and remove

**Test** signs in with the key pair. The card then shows "Signs in as" followed by the user and role that Snowflake returns, for example "AI\_AGENT with role AI\_AGENT\_ROLE", the method **Key pair**, and when it was tested and added. Test proves that the key pair, role and warehouse work. It does not check what the role can read.

<Frame>
  <img src="https://mintcdn.com/lightdash/18J8lppse3Q52tRw/images/agents/agent-identity/snowflake-ai-service-account-card.png?fit=max&auto=format&n=18J8lppse3Q52tRw&q=85&s=8dfba8986846002d2b42fdde8d4b6f97" alt="The shared agent account card on a Snowflake project, marked In use: Agents on this project run as this account. The organization rule for Snowflake requires it, an Organization settings link, Signs in as AI_AGENT with role AI_AGENT_ROLE, Key pair, Tested 9 Oct 2026. Added 9 Oct 2026, and Test, Replace and Remove buttons" width="1992" height="812" data-path="images/agents/agent-identity/snowflake-ai-service-account-card.png" />
</Frame>

* To change the user, key, role or warehouse, select **Replace**. A failed sign-in keeps the saved account.
* To remove the shared agent account, select **Remove** and confirm. Agent queries on that project are refused until an admin adds one again.

Preview projects use the shared agent account of their parent project. See [Preview projects](#preview-projects).

### What agents and people see on Snowflake

When the Snowflake rule is **A shared agent account**:

* Chat, [MCP](/agents/lightdash-mcp), [Slack](/integrations/slack) and data app sample queries on Snowflake run as the shared agent account.
* Agent sessions turn off the Snowflake result cache, and Lightdash does not reuse cached results for these queries.
* People have nothing to set up. They do not connect an agent.

When a project has no shared agent account, or Snowflake rejects the key pair, Lightdash refuses the agent query. See [When the shared agent account is missing or fails](#when-the-shared-agent-account-is-missing-or-fails).

### Verify the Snowflake shared agent account

Ask an agent to run this SQL, for example in [SQL mode](/agents/use-ai-agents#sql-mode) or through the MCP `run_sql` tool:

```sql theme={null}
SELECT CURRENT_USER() AS principal,
       CURRENT_ROLE() AS role,
       SYS_CONTEXT('SNOWFLAKE$CURRENT', 'IS_AGENT_ACTIVATED') AS agent_activated;
```

The result is the shared agent account user and role. With `TYPE = SERVICE_AGENT`, `agent_activated` is true.

## BigQuery: the shared agent account

With this identity, agent queries on a BigQuery project run as a separate Google Cloud service account. An admin adds the account's key to the project connection. BigQuery then applies that account's grants to every agent query, so you decide what agents can read with ordinary BigQuery permissions.

<Warning>
  Every person's agent gets the same access: the grants of the shared agent account. BigQuery does not apply each person's own row-level access policies or column masking to agent queries. Grant the shared agent account only the data that every agent user may see. You can write row access policies and masking rules that target the shared agent account itself. See [Decide what agents can read](#decide-what-agents-can-read).
</Warning>

The shared agent account replaces only the credentials of the connection. The project, dataset and location stay from the connection.

### Set up the BigQuery shared agent account

While the project has no shared agent account, the **Shared agent account** card on **Project settings → Agent identity** shows three setup steps: create the account in BigQuery, grant it only the data agents may read, and add it here and select **Test**. The commands create the account, grant `roles/bigquery.jobUser` on the project that runs the jobs, and grant `roles/bigquery.dataViewer` on the dataset of the connection. The project, the execution project and the dataset come from the connection. Select the copy button to copy the commands.

<Frame>
  <img src="https://mintcdn.com/lightdash/18J8lppse3Q52tRw/images/agents/agent-identity/ai-service-account-setup.png?fit=max&auto=format&n=18J8lppse3Q52tRw&q=85&s=31fe8deb7b7f9e7b4abfb65518434e1d" alt="The shared agent account card on a BigQuery project with no account: the alert Agents are refused on this project until you add a shared agent account, an Add shared agent account button, and the setup steps with gcloud and bq commands in steps 1 and 2 and Add it here and select Test in step 3" width="1992" height="1584" data-path="images/agents/agent-identity/ai-service-account-setup.png" />
</Frame>

The commands use the account name `lightdash-agents`. Change it if your organization uses a different name.

<Steps>
  <Step title="Create a service account">
    In Google Cloud, create a service account for agents. Use a different account from the one the project connection uses, so agents get their own grants. See [Create service accounts](https://cloud.google.com/iam/docs/service-accounts-create).
  </Step>

  <Step title="Grant read access">
    Grant the account `roles/bigquery.jobUser` on the Google Cloud project of the connection, so that it can run queries. Then grant it read access only to the data that agents may use, for example `roles/bigquery.dataViewer` on each dataset. Do not grant data roles on the whole project unless agents may read every dataset in it. See [Decide what agents can read](#decide-what-agents-can-read) and [BigQuery IAM roles](https://cloud.google.com/bigquery/docs/access-control).
  </Step>

  <Step title="Create a JSON key">
    Create a JSON key for the account and download the key file. See [Create and delete service account keys](https://cloud.google.com/iam/docs/keys-create-delete).
  </Step>

  <Step title="Add the key in Lightdash">
    A project admin goes to **Project settings → Agent identity** and finds the **Shared agent account** card. Select **Add shared agent account**, then select the JSON key file in **Service account key file**.
  </Step>

  <Step title="Test the key">
    Select **Test**. Lightdash runs `SELECT SESSION_USER()` with the key and shows "Signs in as" followed by the account that BigQuery returns. Check that this is the shared agent account and not the connection's own account. Test saves nothing.
  </Step>

  <Step title="Save">
    Select **Save**. An "Shared agent account saved." notification confirms it.
  </Step>

  <Step title="Set the BigQuery rule">
    Add a key on every BigQuery project connection first. Then an organization admin selects the BigQuery chip on **Organization settings → Agents** and chooses **A shared agent account**. See [Choose who agents run as](#choose-who-agents-run-as).
  </Step>
</Steps>

### The project Agent identity page

People who can manage a project find **Agent identity** in the project settings sidebar, after **Tables configuration**. The page has one **Shared agent account** card, with the same layout on Snowflake, BigQuery, Databricks, Athena, Postgres, Redshift, Trino and ClickHouse:

* **Status.** A badge says **In use** or **Not in use**, and one line gives the reason from the organization rule, for example "Agents on this project run as this account. The organization rule for BigQuery requires it." An **Organization settings** link opens the rule. Only an organization admin can change it.
* **Missing.** When the rule requires a shared agent account and the project has none, an alert says "Agents are refused on this project until you add a shared agent account." The card has an **Add shared agent account** button.
* **Summary.** After you save an account, the card shows "Signs in as" followed by the account, the method (**Key file**, **Key pair** or **Client ID and secret**), and when it was tested and added. Before the first Test, it says "Not tested yet. Select Test to see who it signs in as."
* **Buttons.** **Test**, **Replace** and **Remove**. A failed Test shows the error in an alert.
* **Setup steps.** While the project has no shared agent account, the card shows three setup steps for the warehouse. After you save an account, the card does not show them. A [preview project](#preview-projects) never shows them, because its account comes from the parent project.

<Frame>
  <img src="https://mintcdn.com/lightdash/18J8lppse3Q52tRw/images/agents/agent-identity/project-ai-service-account-in-use.png?fit=max&auto=format&n=18J8lppse3Q52tRw&q=85&s=c59b8a727b85a171936190a8165de9b8" alt="The shared agent account card on a BigQuery project, marked In use: Agents on this project run as this account. The organization rule for BigQuery requires it, an Organization settings link, Not tested yet, Key file, Added 9 Oct 2026 and Test, Replace and Remove buttons" width="1992" height="812" data-path="images/agents/agent-identity/project-ai-service-account-in-use.png" />
</Frame>

When the rule is **The person**, the card says **Not in use**, and agents use each person's own credentials. When the Snowflake rule is **The person's agent sign-in**, the card says agents use each person's own agent sign-in for Snowflake.

Lightdash does not check that the shared agent account differs from the account of the connection. Use the Test result to confirm it. To check what agents can read, see [Verify on BigQuery](#verify-on-bigquery).

### Decide what agents can read

The shared agent account reads only what you grant it. BigQuery checks those grants on every agent query, so you restrict agents with BigQuery permissions and policies on the shared agent account. You can combine the levels below.

The examples use `lightdash-agents@my-project.iam.gserviceaccount.com` for the shared agent account and `lightdash@my-project.iam.gserviceaccount.com` for the connection's own account. Replace them with your accounts.

<Warning>
  Agents query the same explores, and so the same tables, as people. Put each restriction on the tables behind those explores. A question that needs data the shared agent account cannot read fails with an access denied error in chat, MCP and Slack. Keep the explores that agents use within what the shared agent account can read. To hide explores and fields from agents, use [agent tags](/agents/data-access#limiting-access-to-specific-explores-and-fields).
</Warning>

#### Datasets

Grant `roles/bigquery.dataViewer` only on the datasets that agents may use:

```sql theme={null}
GRANT `roles/bigquery.dataViewer`
ON SCHEMA `my-project`.jaffle_shop
TO "serviceAccount:lightdash-agents@my-project.iam.gserviceaccount.com";
```

The account can then query every table in that dataset, and no table in a dataset where it has no grant. See [GRANT statement](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-control-language#grant_statement).

#### Tables

To give agents only some tables in a dataset, grant Data Viewer on each table instead of on the dataset:

```sql theme={null}
GRANT `roles/bigquery.dataViewer`
ON TABLE `my-project`.jaffle_shop.orders
TO "serviceAccount:lightdash-agents@my-project.iam.gserviceaccount.com";
```

See [table-level access control](https://cloud.google.com/bigquery/docs/table-access-controls-intro).

#### Columns

Policy tags protect individual columns. You create a taxonomy with a policy tag, for example `pii`, and attach it to the sensitive columns. Then you choose what the shared agent account sees in those columns:

* **Masked values.** Add a data policy with a masking rule to the policy tag. Grant the shared agent account the Masked Reader role, `roles/bigquerydatapolicy.maskedReader`, on the data policy. The account then reads masked values: for example `NULL` with the Nullify rule, or an empty string for a `STRING` column with the Default masking value rule. This is the BigQuery equivalent of the Snowflake `REDACTED` example.
* **No access.** Do not grant the shared agent account the Fine-Grained Reader role, `roles/datacatalog.categoryFineGrainedReader`, or the Masked Reader role. A query that selects the column fails with an error. Agent questions that do not use the column still work.

For example, with the Nullify rule on the `email` column of `protected_records`:

| Account | Role | `SELECT email FROM protected_records` |
| - | - | - |
| The connection's own account | Fine-Grained Reader on the policy tag | The real email addresses |
| The shared agent account | Masked Reader on the data policy | `NULL` in every row |
| The shared agent account | Neither role | An access denied error |

When a policy tag has a data policy, or its taxonomy enforces access control, every account that reads the column needs one of the two roles. Grant Fine-Grained Reader on the policy tag to the connection's own account, and to people who query with their own Google accounts. Otherwise their dashboards fail on that column too.

See [column-level access control](https://cloud.google.com/bigquery/docs/column-level-security-intro) and [dynamic data masking](https://cloud.google.com/bigquery/docs/column-data-masking-intro).

#### Rows

A row access policy limits the rows that the shared agent account can read. The account still needs Data Viewer on the table:

```sql theme={null}
CREATE ROW ACCESS POLICY agent_rows
ON `my-project.jaffle_shop.protected_records`
GRANT TO ('serviceAccount:lightdash-agents@my-project.iam.gserviceaccount.com')
FILTER USING (is_sensitive = FALSE);
```

<Warning>
  When a table has a row access policy, accounts that no policy grants see no rows. This includes the connection's own account. Add a policy with the `TRUE` filter for that account, and for people who query with their own Google accounts.
</Warning>

```sql theme={null}
CREATE ROW ACCESS POLICY connection_all_rows
ON `my-project.jaffle_shop.protected_records`
GRANT TO ('serviceAccount:lightdash@my-project.iam.gserviceaccount.com')
FILTER USING (TRUE);
```

See [row-level security](https://cloud.google.com/bigquery/docs/row-level-security-intro), [manage row access policies](https://cloud.google.com/bigquery/docs/managing-row-level-security) and [the `TRUE` filter](https://cloud.google.com/bigquery/docs/using-row-level-security-with-features#the_true_filter).

Column-level access control, dynamic data masking and row-level security need BigQuery Enterprise or Enterprise Plus edition, or on-demand pricing. See [BigQuery editions](https://cloud.google.com/bigquery/docs/editions-intro).

### One key for each project connection

Each project connection holds its own shared agent account. While the BigQuery rule is **A shared agent account**, agents are refused on every BigQuery project that has no key. A preview project uses the key of its parent, see [Preview projects](#preview-projects). Add a key on every BigQuery project connection. You can use the same key file on more than one project.

Each chip on **Organization settings → Agents** that uses **A shared agent account** shows the number of projects that have no shared agent account yet. When every project has one, the chip shows a green tick. See [Read the chips](#read-the-chips).

#### Preview projects

A preview project always uses the shared agent account of its parent project for the matching connection. A preview has no shared agent account of its own, and you cannot add one on a preview. Developers can ask agents about a preview with no extra setup. Lightdash does not copy the account: it reads the account of the parent on each query. When an admin replaces or removes the account of the parent, previews use the new state on their next query.

The **Shared agent account** card on the preview's **Agent identity** page is read-only. To add or change the account, go to the parent project.

* **The parent has an account.** The card says "Uses the shared agent account from" followed by a link to the parent. It shows "Signs in as" followed by the account, the method, and when it was tested. Select **Test** to test the parent's account on the preview's connection.
* **The parent has no account, and the rule requires one.** A warning says "Agents are refused on this preview until the parent project has a shared agent account for this connection. Ask an admin to add one on the parent project." The last words link to the parent project.
* **The parent has no account, and the rule does not require one.** The card says "The parent project has no shared agent account. Add one on the parent project."
* **Lightdash can't read the parent's account.** An alert says that agents are refused on the preview. Ask an admin of the parent project to replace the parent's account.

<Frame>
  <img src="https://mintcdn.com/lightdash/cqFtbc_Jed_Xh3cL/images/agents/agent-identity/preview-parent-shared-agent-account.png?fit=max&auto=format&n=cqFtbc_Jed_Xh3cL&q=85&s=3e598808a860130c6471e9f176fcf599" alt="The shared agent account card on a Postgres preview project, marked In use: Agents on this project run as this account. The organization rule for Postgres requires it, an Organization settings link, Uses the shared agent account from Jaffle shop, Signs in as followed by the account, User and password, Tested 11 Oct 2026, and a Test button" width="1976" height="816" data-path="images/agents/agent-identity/preview-parent-shared-agent-account.png" />
</Frame>

<Frame>
  <img src="https://mintcdn.com/lightdash/cqFtbc_Jed_Xh3cL/images/agents/agent-identity/preview-parent-no-shared-agent-account.png?fit=max&auto=format&n=cqFtbc_Jed_Xh3cL&q=85&s=d12715ac8ff99e72dab037180cec84c5" alt="The shared agent account card on a preview project with the warning Agents are refused on this preview until the parent project has a shared agent account for this connection. Ask an admin to add one on the parent project" width="1976" height="556" data-path="images/agents/agent-identity/preview-parent-no-shared-agent-account.png" />
</Frame>

The original connection of a preview matches the original connection of the parent. An extra connection matches the extra connection of the parent with the same name and warehouse type. A preview does not use the parent's account when no connection matches or when the parent is in another organization. The preview then has no shared agent account. A preview of a preview uses the account of its direct parent. A preview whose parent project was deleted is an ordinary project and keeps its own shared agent account.

Anyone who can query a preview can make agents run as the parent's shared agent account on that preview. Only admins of the parent can change the parent's account.

**Grant the parent's account access to preview data.** Agent queries on a preview run as the parent's account, so that account needs read access to the datasets or schemas that previews build into. Without it, agent queries on the preview fail:

* **Postgres, Redshift, Trino and ClickHouse:** the error starts with "This preview uses the parent's shared agent account, which can't read this preview's data. Ask an admin to grant it access.", followed by the warehouse error.
* **BigQuery, Snowflake, Databricks and Athena:** the person sees the warehouse error only.

### What agents and people see

When the BigQuery rule is **A shared agent account**:

* Chat, [MCP](/agents/lightdash-mcp), [Slack](/integrations/slack) and data app sample queries on BigQuery run as the shared agent account.
* BigQuery agent jobs carry the label `agent: true` and skip the BigQuery query cache. Lightdash does not reuse cached results for these queries.
* **Settings → My agent identity** shows a BigQuery card that says agents run as the shared agent account your admin set up. People have nothing to set up. See [My agent identity](#my-agent-identity).

When a project has no shared agent account, Lightdash refuses the agent query and blocks AI composers on the project. The shared agent account card on the project's **Agent identity** page shows a warning for admins. See [When the shared agent account is missing or fails](#when-the-shared-agent-account-is-missing-or-fails).

When BigQuery rejects the saved key, Lightdash refuses the agent query. Replace the key on the shared agent account card.

To remove the key, select **Remove** on the card and confirm. Agent queries on that project are refused until an admin adds a key again.

### Verify on BigQuery

Ask an agent to run this SQL, for example in [SQL mode](/agents/use-ai-agents#sql-mode) or through the MCP `run_sql` tool:

```sql theme={null}
SELECT SESSION_USER() AS principal;
```

The result is the email address of the shared agent account.

Then find recent agent jobs in the jobs view for the region of your data. Run it as an account that can run queries and list all jobs in the project, for example one with the BigQuery Resource Viewer and BigQuery Job User roles:

```sql theme={null}
SELECT creation_time, user_email, job_id, query
FROM `region-us`.INFORMATION_SCHEMA.JOBS
WHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)
  AND EXISTS (
    SELECT 1 FROM UNNEST(labels) AS label
    WHERE label.key = 'agent' AND label.value = 'true'
  )
ORDER BY creation_time DESC;
```

Replace `region-us` with the region of your datasets. Agent jobs show the shared agent account in `user_email`. See [JOBS view](https://cloud.google.com/bigquery/docs/information-schema-jobs).

Finally, ask an agent about an explore whose table the shared agent account cannot read. BigQuery refuses the query with an access denied error.

## Databricks: the shared agent account

With this identity, agent queries on a Databricks project run as a separate Databricks service principal. An admin adds the service principal's OAuth client ID and secret (OAuth machine-to-machine) to the project connection. Unity Catalog then applies that service principal's grants, row filters and column masks to every agent query.

<Warning>
  Every person's agent gets the same access: the grants of the service principal. Unity Catalog applies its row filters and column masks to the service principal, not to the person who asked. Grant the service principal only the data that every agent user may see.
</Warning>

Databricks does not offer **The person's agent sign-in**, and has no service principal for each group.

The shared agent account replaces only the sign-in of the connection. The host, HTTP path, catalog, schema and compute stay from the connection.

### Before you start

* The `agent-identity` feature flag is on. See the note at the top of this page.
* You are an admin of the Lightdash project, and an organization admin sets the rule.
* The project connection uses a Databricks SQL warehouse in the workspace.

### Set up the Databricks shared agent account

While the project has no shared agent account, the **Shared agent account** card on **Project settings → Agent identity** shows three setup steps: create the account in Databricks, grant it only the data agents may read, and add it here and select **Test**.

<Steps>
  <Step title="Create the service principal and an OAuth secret">
    In the Databricks account console, create a service principal and an OAuth secret for it. Add the service principal to the workspace, and give it the `CAN USE` permission on the SQL warehouse of the connection. The application ID of the service principal is the client ID. See [Service principals](https://docs.databricks.com/aws/en/admin/users-groups/service-principals) and [OAuth machine-to-machine authentication](https://docs.databricks.com/aws/en/dev-tools/auth/oauth-m2m).
  </Step>

  <Step title="Grant access to the data agents may read">
    Grant the service principal `USE CATALOG`, `USE SCHEMA` and `SELECT`. The card has the statements with the catalog and schema of the connection, and a copy button. Replace the placeholders, and repeat `SELECT` for each table or view that agents may read:

    ```sql theme={null}
    GRANT USE CATALOG ON CATALOG `analytics` TO `<service-principal-application-id>`;
    GRANT USE SCHEMA ON SCHEMA `analytics`.`marts` TO `<service-principal-application-id>`;
    GRANT SELECT ON TABLE `analytics`.`marts`.`<table>` TO `<service-principal-application-id>`;
    ```
  </Step>

  <Step title="Add the client ID and secret">
    Select **Add shared agent account**. Enter the service principal's application ID in **Client ID** and the **Client secret**, then select **Test and save**. **Test** checks the values without saving them.
  </Step>

  <Step title="Set the Databricks rule">
    Add a shared agent account on every Databricks project connection first. Then an organization admin selects the Databricks chip on **Organization settings → Agents** and chooses **A shared agent account**. See [Choose who agents run as](#choose-who-agents-run-as).
  </Step>
</Steps>

<Frame>
  <img src="https://mintcdn.com/lightdash/18J8lppse3Q52tRw/images/agents/agent-identity/databricks-ai-service-account-card.png?fit=max&auto=format&n=18J8lppse3Q52tRw&q=85&s=69a270af808e397317b0de6df9374ba0" alt="The shared agent account card on a Databricks project with no account: the alert Agents are refused on this project until you add a shared agent account, an Add shared agent account button, and the setup steps with the service principal steps, the GRANT statements and a copy button, and Add it here and select Test" width="1992" height="1340" data-path="images/agents/agent-identity/databricks-ai-service-account-card.png" />
</Frame>

<Frame>
  <img src="https://mintcdn.com/lightdash/A_i9b2kOx0HGgLi6/images/agents/agent-identity/databricks-ai-service-account-form.png?fit=max&auto=format&n=A_i9b2kOx0HGgLi6&q=85&s=0b85094e11696f29b6b6481b3b14cf15" alt="The shared agent account form with Client ID and Client secret fields and Cancel, Test and Test and save buttons" width="716" height="382" data-path="images/agents/agent-identity/databricks-ai-service-account-form.png" />
</Frame>

### Test, replace and remove

**Test** signs in as the service principal and runs `SELECT current_user()`. The card then shows "Signs in as" followed by the application ID, the method **Client ID and secret**, and when it was tested and added. Test proves that the client ID and secret work. It does not check what the service principal can read.

* To change the service principal or the secret, select **Replace**.
* Databricks OAuth secrets expire. Create a new secret before the old one expires, then select **Replace** and enter it.
* To remove the shared agent account, select **Remove** and confirm. Agent queries on that project are refused until an admin adds one again.

Preview projects use the shared agent account of their parent project. See [Preview projects](#preview-projects).

### What agents and people see on Databricks

When the Databricks rule is **A shared agent account**:

* Chat, [MCP](/agents/lightdash-mcp), [Slack](/integrations/slack) and data app sample queries on Databricks run as the service principal.
* Agent queries skip the Databricks result cache.
* People have nothing to set up.

When a project has no shared agent account, or Databricks rejects the client ID or secret, Lightdash refuses the agent query. See [When the shared agent account is missing or fails](#when-the-shared-agent-account-is-missing-or-fails).

### Verify on Databricks

Ask an agent to run this SQL, for example in [SQL mode](/agents/use-ai-agents#sql-mode) or through the MCP `run_sql` tool:

```sql theme={null}
SELECT current_user() AS principal;
```

The result is the application ID of the service principal. Then ask an agent about an explore whose table the service principal cannot read. Databricks refuses the query with a permission error.

## Athena: the shared agent account

With this identity, agent queries on an Athena project run as a separate AWS identity, with its own Athena workgroup and its own S3 results location. An admin adds the identity's access keys to the project connection. Lake Formation grants and data filters on that identity then decide what agents can read.

<Warning>
  Every person's agent gets the same access: the grants of the shared agent account. Grant it only the data that every agent user may see.
</Warning>

The shared agent account takes only the region, catalog, database and query settings from the connection. The keys, workgroup and S3 locations come from the shared agent account. The connection's own role, web identity, keys, workgroup and S3 locations never apply to agent queries.

### Choose the AWS identity

Lightdash signs in with access keys. It cannot renew keys yet.

* **A dedicated IAM user for agents (recommended for now).** Give the user only the permissions agents need, and use its long-lived access keys. Rotate the keys on your normal schedule, and select **Replace** to add the new keys.
* **An IAM role.** The setup steps in the card create a role. Keys from an assumed role expire within hours. When they expire, Lightdash refuses agent queries until an admin adds new keys.

Lightdash will support keyless sign-in later.

### Set up the Athena shared agent account

While the project has no shared agent account, the **Shared agent account** card on **Project settings → Agent identity** shows three setup steps. It has copy buttons for each policy and grant, built from the connection's region, catalog and database.

<Steps>
  <Step title="Create the AWS identity">
    Create an IAM user or role for agents. Create an Athena workgroup and an S3 results location for agents only. Attach the **Permission policy** from the setup steps. It allows queries through the agent workgroup, reads of the catalog metadata, and the results location. For a role, also use the **Role trust policy**, and give the trusted caller permission to assume the role. KMS keys and cross-account catalogs need extra permissions. See [Athena workgroups](https://docs.aws.amazon.com/athena/latest/ug/workgroups-manage-queries-control-costs.html).
  </Step>

  <Step title="Grant it only the data agents may read">
    As a Lake Formation administrator, run the **Lake Formation grant** from the setup steps for each table that agents may read. Grant to the user or role ARN, not to a session ARN. To limit rows or columns, use the **Filtered table grant** with a data filter instead. Other grants on the table can still allow more. See [Lake Formation data filters](https://docs.aws.amazon.com/lake-formation/latest/dg/data-filters-about.html).
  </Step>

  <Step title="Add the shared agent account">
    Select **Add shared agent account**. Enter the **Access key ID** and **Secret access key**, the **Session token** if the keys come from an assumed role, the **Agent workgroup**, the **S3 results location** and, if needed, the **S3 data location**. Select **Test and save**. **Test** checks the values without saving them.
  </Step>

  <Step title="Set the Athena rule">
    Add a shared agent account on every Athena project connection first. Then an organization admin selects the Athena chip on **Organization settings → Agents** and chooses **A shared agent account**, and confirms. See [Choose who agents run as](#choose-who-agents-run-as).
  </Step>
</Steps>

<Frame>
  <img src="https://mintcdn.com/lightdash/Sv3K_3O_8GJxQJtG/images/agents/agent-identity/athena-ai-service-account-form.png?fit=max&auto=format&n=Sv3K_3O_8GJxQJtG&q=85&s=388cb531772a1be027109293dd85fc01" alt="The shared agent account form for Athena with Access key ID, Secret access key, Session token (optional), Agent workgroup, S3 results location and S3 data location (optional) fields, and Cancel, Test and Test and save buttons" width="716" height="658" data-path="images/agents/agent-identity/athena-ai-service-account-form.png" />
</Frame>

### Test, replace and remove

**Test** checks the AWS identity with `GetCallerIdentity`, then runs a simple query through the agent workgroup. The card then shows "Signs in as" followed by the identity's ARN, the method **Access keys**, and when it was tested and added. Test proves that the keys, workgroup and results location work. It does not check table grants. Test gives a specific message for rejected keys, denied access, a results location problem and a missing workgroup.

<Frame>
  <img src="https://mintcdn.com/lightdash/Sv3K_3O_8GJxQJtG/images/agents/agent-identity/athena-ai-service-account-card.png?fit=max&auto=format&n=Sv3K_3O_8GJxQJtG&q=85&s=06dc459178c4763387ae908000edb64c" alt="The shared agent account card on an Athena project, marked In use: Agents on this project run as this account. The organization rule for Athena requires it, an Organization settings link, Signs in as followed by an assumed-role ARN, Access keys, Tested 9 Oct 2026. Added 8 Oct 2026, and Test, Replace and Remove buttons" width="996" height="406" data-path="images/agents/agent-identity/athena-ai-service-account-card.png" />
</Frame>

* To add new keys, select **Replace**.
* To remove the shared agent account, select **Remove** and confirm. Agent queries on that project are refused until an admin adds one again.

Preview projects use the shared agent account of their parent project. See [Preview projects](#preview-projects).

### What agents and people see on Athena

When the Athena rule is **A shared agent account**:

* Chat, [MCP](/agents/lightdash-mcp), [Slack](/integrations/slack) and data app sample queries on Athena run as the shared agent account, through its workgroup.
* **Settings → My agent identity** shows an Athena card. People have nothing to set up. See [My agent identity](#my-agent-identity).

When a project has no shared agent account, or AWS rejects the keys, Lightdash refuses the agent query. Expired keys from an assumed role cause this too. See [When the shared agent account is missing or fails](#when-the-shared-agent-account-is-missing-or-fails). Lightdash never falls back to the connection's own credentials.

## Postgres: the shared agent account

With this identity, agent queries on a Postgres project sign in as a separate Postgres login role, with its own user and password. Postgres then applies that role's grants and row-level security policies to every agent query.

<Warning>
  Every person's agent gets the same access: the grants of the shared agent account's role. Grant it only the data that every agent user may see.
</Warning>

The shared agent account replaces only the user and password of the connection. The host, port, database, schema, SSL mode, root certificate and SSH tunnel stay from the connection. The connection's client certificates and its `role` setting never apply to agent queries.

### Set up the Postgres shared agent account

While the project has no shared agent account, the **Shared agent account** card on **Project settings → Agent identity** shows three setup steps, with a **Copy SQL** button for each block. The SQL uses the database and schema of the connection.

<Steps>
  <Step title="Create a read-only login role">
    As a superuser or a role with `CREATEROLE`, create a separate login role for agents. Choose a strong password:

    ```sql theme={null}
    CREATE ROLE "ai_agents" LOGIN PASSWORD '<choose-a-strong-password>'
      NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS;
    ALTER ROLE "ai_agents" SET default_transaction_read_only = on;
    GRANT CONNECT ON DATABASE "<database>" TO "ai_agents";
    ```
  </Step>

  <Step title="Grant the schemas that agents may read">
    Grant only the data that every agent user may see:

    ```sql theme={null}
    GRANT USAGE ON SCHEMA "<schema>" TO "ai_agents";
    GRANT SELECT ON ALL TABLES IN SCHEMA "<schema>" TO "ai_agents";
    ALTER DEFAULT PRIVILEGES IN SCHEMA "<schema>" GRANT SELECT ON TABLES TO "ai_agents";
    ```

    `ALTER DEFAULT PRIVILEGES` applies only to new tables that the role running it creates. Run it as each role that creates tables.
  </Step>

  <Step title="Add the shared agent account">
    Select **Add shared agent account**. Enter the **User** and **Password** of the login role, then select **Test and save**. **Test** checks the values without saving them.
  </Step>

  <Step title="Set the Postgres rule">
    Add a shared agent account on every Postgres project connection first. Then an organization admin selects the Postgres chip on **Organization settings → Agents** and chooses **A shared agent account**, and confirms. See [Choose who agents run as](#choose-who-agents-run-as).
  </Step>
</Steps>

<Frame>
  <img src="https://mintcdn.com/lightdash/iSpJAup9JhRthojh/images/agents/agent-identity/postgres-ai-service-account-setup.png?fit=max&auto=format&n=iSpJAup9JhRthojh&q=85&s=e2b6375cebb0fd63e19be1260fbb94b3" alt="The shared agent account card on a Postgres project with no account, marked Not in use, with an Add shared agent account button and the three setup steps: Create the account in Postgres with the CREATE ROLE SQL, Grant it only the data agents may read with the GRANT statements and the row-level security template, and Add it here and select Test" width="2071" height="1858" data-path="images/agents/agent-identity/postgres-ai-service-account-setup.png" />
</Frame>

### Limit rows and columns

To limit the rows that agents read, add a row-level security policy for the role. Replace the table and condition placeholders:

```sql theme={null}
ALTER TABLE "<schema>"."<table>" ENABLE ROW LEVEL SECURITY;
CREATE POLICY "ai_agents_rows" ON "<schema>"."<table>"
  FOR SELECT TO "ai_agents" USING (<condition>);
```

Row-level security does not apply to superusers, to roles with `BYPASSRLS`, or to the table owner unless the table has `FORCE ROW LEVEL SECURITY`. See [Row security policies](https://www.postgresql.org/docs/current/ddl-rowsecurity.html).

To hide sensitive columns, replace the table-wide `SELECT` grant with a column grant, for example `GRANT SELECT (col_a, col_b) ON "<schema>"."<table>" TO "ai_agents";`. A table-wide grant still allows every column.

### Test, replace and remove

**Test** signs in as the login role and runs `SELECT current_user, session_user`. Test fails when either value differs from the saved user. The card then shows "Signs in as" followed by the user, and when it was tested and added. Test proves that the user and password work. It does not check what the role can read.

When Test fails, the card gives a fixed message for each cause: rejected credentials, a role without `LOGIN`, a `pg_hba.conf` rule that blocks the user, no `CONNECT` on the database, or a database that does not exist. Lightdash never shows the driver message, so a password never reaches the page or the logs.

* To change the user or password, select **Replace**.
* To remove the shared agent account, select **Remove** and confirm. Agent queries on that project are refused until an admin adds one again.

Preview projects use the shared agent account of their parent project. See [Preview projects](#preview-projects).

### What agents and people see on Postgres

When the Postgres rule is **A shared agent account**:

* Chat, [MCP](/agents/lightdash-mcp), [Slack](/integrations/slack) and data app sample queries on Postgres sign in as the shared agent account. On a connection with an SSH tunnel, they use the same tunnel.
* **Settings → My agent identity** shows a Postgres card. People have nothing to set up. See [My agent identity](#my-agent-identity).

When a project has no shared agent account, or Postgres rejects the user or password, Lightdash refuses the agent query. See [When the shared agent account is missing or fails](#when-the-shared-agent-account-is-missing-or-fails). Lightdash never falls back to the connection's own credentials.

## Redshift: the shared agent account

With this identity, agent queries on a Redshift project sign in as a separate Redshift database user, with its own user and password. Redshift then applies that user's grants, row-level security and masking policies to every agent query.

<Warning>
  Every person's agent gets the same access: the grants of the shared agent account. Grant it only the data that every agent user may see. Do not give the user superuser, `IGNORE RLS` or `sys:secadmin` rights, and check its inherited and `PUBLIC` grants.
</Warning>

The shared agent account always signs in with a user and password, also when the project connection uses IAM. The host, port, database, schema, SSL settings and SSH tunnel stay from the connection. No IAM, SSO or personal sign-in fields apply to agent queries.

### Set up the Redshift shared agent account

While the project has no shared agent account, the **Shared agent account** card on **Project settings → Agent identity** shows three setup steps, with a **Copy SQL** button for each block. The SQL uses the schema of the connection.

<Steps>
  <Step title="Create the database user">
    As a superuser or a user with `CREATE USER`, create a separate user for agents. Use a password of 8 to 64 ASCII characters, with an uppercase letter, a lowercase letter and a number. Do not use spaces, quotes, backslashes, slashes or `@`:

    ```sql theme={null}
    CREATE USER ai_agents
      PASSWORD '<choose-a-strong-password>'
      NOCREATEDB NOCREATEUSER
      SYSLOG ACCESS RESTRICTED;
    ```
  </Step>

  <Step title="Grant the schemas that agents may read">
    In the project database, grant only the data that every agent user may see:

    ```sql theme={null}
    GRANT USAGE ON SCHEMA "<schema>" TO ai_agents;
    GRANT SELECT ON ALL TABLES IN SCHEMA "<schema>" TO ai_agents;
    ALTER DEFAULT PRIVILEGES FOR USER "<table-owner>"
      IN SCHEMA "<schema>"
      GRANT SELECT ON TABLES TO ai_agents;
    ```

    `ALTER DEFAULT PRIVILEGES` grants access to future tables of one owner. Repeat it for each table owner.
  </Step>

  <Step title="Add the shared agent account">
    Select **Add shared agent account**. Enter the **User** and **Password**, then select **Test and save**. **Test** checks the values without saving them.
  </Step>

  <Step title="Set the Redshift rule">
    Add a shared agent account on every Redshift project connection first. Then an organization admin selects the Redshift chip on **Organization settings → Agents** and chooses **A shared agent account**, and confirms. See [Choose who agents run as](#choose-who-agents-run-as).
  </Step>
</Steps>

<Frame>
  <img src="https://mintcdn.com/lightdash/xuCDqJqPKyMD_I0w/images/agents/agent-identity/redshift-ai-service-account-setup.png?fit=max&auto=format&n=xuCDqJqPKyMD_I0w&q=85&s=0ebd6616787cb5c76885fb6eea8fade2" alt="The shared agent account card on a Redshift project with no account: the alert Agents are refused on this project until you add a shared agent account, an Add shared agent account button, and the setup steps with the CREATE USER and GRANT statements" width="996" height="892" data-path="images/agents/agent-identity/redshift-ai-service-account-setup.png" />
</Frame>

### Limit rows and columns on Redshift

The setup steps have templates for a row-level security policy and a masking policy. Replace the table, column, type and condition, and match the mask type to the column:

```sql theme={null}
CREATE RLS POLICY ai_agents_rows
  WITH ("<column>" <type>)
  USING (<condition>);
ATTACH RLS POLICY ai_agents_rows ON "<schema>"."<table>" TO ai_agents;
ALTER TABLE "<schema>"."<table>" ROW LEVEL SECURITY ON;

CREATE MASKING POLICY ai_agents_mask
  WITH (value VARCHAR(256))
  USING ('[redacted]'::VARCHAR(256));
ATTACH MASKING POLICY ai_agents_mask ON "<schema>"."<table>" ("<column>") TO ai_agents;
```

Check the policy settings and limits for external and shared tables. See [Row-level security](https://docs.aws.amazon.com/redshift/latest/dg/t_rls.html) and [Dynamic data masking](https://docs.aws.amazon.com/redshift/latest/dg/t_ddm.html).

### Test, replace and remove

**Test** signs in as the user and runs `SELECT current_user, session_user`. Both values must be the same, and must match the saved user. Redshift folds unquoted user names to lower case, so the lower-case form of the saved user also matches. The card then shows "Signs in as" followed by the user, and when it was tested and added. Test proves that the user and password work. It does not check what the user can read.

When Test fails, the card gives a fixed message: rejected credentials, a missing database, missing grants, or a general message. Lightdash never shows the driver message.

* To change the user or password, select **Replace**.
* To remove the shared agent account, select **Remove** and confirm. Agent queries on that project are refused until an admin adds one again.

Preview projects use the shared agent account of their parent project. See [Preview projects](#preview-projects).

### What agents and people see on Redshift

When the Redshift rule is **A shared agent account**:

* Chat, [MCP](/agents/lightdash-mcp), [Slack](/integrations/slack) and data app sample queries on Redshift sign in as the shared agent account. On a connection with an SSH tunnel, they use the same tunnel.
* Agent queries skip the Redshift result cache.
* **Settings → My agent identity** shows a Redshift card. People have nothing to set up. See [My agent identity](#my-agent-identity).

When a project has no shared agent account, or Redshift rejects the user or password, Lightdash refuses the agent query. See [When the shared agent account is missing or fails](#when-the-shared-agent-account-is-missing-or-fails). Lightdash never falls back to the connection's own credentials.

## Trino: the shared agent account

With this identity, agent queries on a Trino project sign in as a separate Trino user, with its own user and password. Trino access control then decides what agents read.

<Warning>
  Every person's agent gets the same access: the access of the shared agent account's user. Grant it only the data that every agent user may see.
</Warning>

The shared agent account replaces only the user and password of the connection. The host, port, scheme, catalog, schema and source stay from the connection. Lightdash signs in to Trino with password (basic) authentication only, so the shared agent account has no JWT, OAuth 2.0, Kerberos or certificate sign-in. A Starburst Galaxy service account works, because it signs in with a user name and password.

### Set up the Trino shared agent account

While the project has no shared agent account, the **Shared agent account** card on **Project settings → Agent identity** shows three setup steps, with copy buttons. The templates use the catalog and schema of the connection.

<Steps>
  <Step title="Create or choose the Trino login">
    Trino has no `CREATE USER`. Ask a Trino admin to add a user to the password file or LDAP. Turn on password authentication over HTTPS. Without it, Trino does not check the password. On Starburst Galaxy, create a service account with a restricted role, and use its user name and generated password.
  </Step>

  <Step title="Limit what the login can read">
    Use file-based access control or SQL roles. See [Limit what the Trino login can read](#limit-what-the-trino-login-can-read).
  </Step>

  <Step title="Add the shared agent account">
    Select **Add shared agent account**. Enter the **User** and **Password**, then select **Test and save**. **Test** checks the values without saving them.
  </Step>

  <Step title="Set the Trino rule">
    Add a shared agent account on every Trino project connection first. Then an organization admin selects the Trino chip on **Organization settings → Agents** and chooses **A shared agent account**, and confirms. See [Choose who agents run as](#choose-who-agents-run-as).
  </Step>
</Steps>

<Frame>
  <img src="https://mintcdn.com/lightdash/AoloVId7eysTjTxd/images/agents/agent-identity/trino-ai-service-account-setup.png?fit=max&auto=format&n=AoloVId7eysTjTxd&q=85&s=1150ed7c671b57314c51ff38d3c41ed2" alt="The shared agent account card on a Trino project, marked Not in use, with an Add shared agent account button and the setup steps: Create or choose the Trino login, and Limit what the login can read with the file-based access control rules.json template" width="996" height="992" data-path="images/agents/agent-identity/trino-ai-service-account-setup.png" />
</Frame>

### Limit what the Trino login can read

**File-based access control.** Set `access-control.name=file`, and point `security.config-file` to a `rules.json` file. The setup steps have a template. It allows read-only access to one catalog, schema and table for the agent user, with an optional row filter and column mask, and denies everything else to that user:

```json theme={null}
{
  "catalogs": [
    { "user": "<user>", "catalog": "<catalog>", "allow": "read-only" },
    { "user": "<user>", "allow": "none" }
  ],
  "schemas": [
    { "user": "<user>", "catalog": "<catalog>", "schema": "<schema>", "owner": false },
    { "user": "<user>", "owner": false }
  ],
  "tables": [
    {
      "user": "<user>",
      "catalog": "<catalog>",
      "schema": "<schema>",
      "table": "<table>",
      "privileges": ["SELECT"],
      "filter": "<row-filter-expression>",
      "columns": [{ "name": "<column>", "mask": "<mask-expression>" }]
    },
    { "user": "<user>", "privileges": [] }
  ]
}
```

Merge these rules above your broader rules. Rules match from top to bottom, and the first match wins. Names in rules are regular expressions, so escape regex characters in your values. The `filter` and `columns` fields are optional. If your authenticator maps the login to another user, use the user that Test shows. See [File-based access control](https://trino.io/docs/current/security/file-system-access-control.html).

**SQL roles and grants.** Use this only with a system access control that supports SQL roles and grants:

```sql theme={null}
CREATE ROLE ai_agents_read;
GRANT SELECT ON TABLE "<catalog>"."<schema>"."<table>" TO ROLE ai_agents_read;
GRANT ai_agents_read TO USER "<user>";
```

Connector roles, such as Hive roles, need `IN "<catalog>"` on `CREATE ROLE` and on the role grant. Check inherited and public grants.

### Test, replace and remove

**Test** signs in and runs `SELECT current_user`. The card then shows "Signs in as" followed by the user that Trino returns, and when it was tested and added. Lightdash does not compare that user with the login, because Trino user mapping can change it. Test proves that the user and password work. It does not check what the user can read.

When Test fails, the card gives a fixed message: rejected credentials, missing access, or a general message. Lightdash never shows the driver message.

* To change the user or password, select **Replace**.
* To remove the shared agent account, select **Remove** and confirm. Agent queries on that project are refused until an admin adds one again.

Preview projects use the shared agent account of their parent project. See [Preview projects](#preview-projects).

### What agents and people see on Trino

When the Trino rule is **A shared agent account**:

* Chat, [MCP](/agents/lightdash-mcp), [Slack](/integrations/slack) and data app sample queries on Trino sign in as the shared agent account.
* Agent queries skip the Lightdash results cache and pre-aggregates. Trino has no result cache setting.
* **Settings → My agent identity** shows a Trino card. People have nothing to set up. See [My agent identity](#my-agent-identity).

When a project has no shared agent account, or Trino rejects the user or password, Lightdash refuses the agent query. See [When the shared agent account is missing or fails](#when-the-shared-agent-account-is-missing-or-fails). Lightdash never falls back to the connection's own credentials.

## ClickHouse: the shared agent account

With this identity, agent queries on a ClickHouse project sign in as a separate read-only ClickHouse user, with its own user and password. ClickHouse then applies that user's grants and row policies to every agent query.

<Warning>
  Every person's agent gets the same access: the grants of the shared agent account. Grant it only the data that every agent user may see.
</Warning>

The shared agent account replaces only the user and password of the connection. The host, port, database, `secure` setting and timezone stay from the connection.

### Set up the ClickHouse shared agent account

While the project has no shared agent account, the **Shared agent account** card on **Project settings → Agent identity** shows three setup steps, with a **Copy SQL** button for each block. The SQL uses the database of the connection.

<Steps>
  <Step title="Create the account in ClickHouse">
    As a user with `CREATE USER` permission, create a separate user for agents. Replace the password placeholder:

    ```sql theme={null}
    CREATE USER ai_agents
      IDENTIFIED WITH sha256_password
      BY '<choose-a-strong-password>'
      SETTINGS readonly = 2;
    ```

    `readonly = 2` keeps the user read-only, and lets agent queries set the query settings they need. Every agent query sends query tags and a session timezone. ClickHouse rejects these settings for a `readonly = 1` user, unless its profile allows them.
  </Step>

  <Step title="Grant it only the data agents may read">
    Grant `SELECT` on the project database, or limit the grant to specific tables:

    ```sql theme={null}
    GRANT SELECT ON `<database>`.* TO ai_agents;
    ```
  </Step>

  <Step title="Add the shared agent account">
    Select **Add shared agent account**. Enter the **User** and **Password**, then select **Test and save**. **Test** checks the values without saving them.
  </Step>

  <Step title="Set the ClickHouse rule">
    Add a shared agent account on every ClickHouse project connection first. Then an organization admin selects the ClickHouse chip on **Organization settings → Agents** and chooses **A shared agent account**, and confirms. See [Choose who agents run as](#choose-who-agents-run-as).
  </Step>
</Steps>

<Frame>
  <img src="https://mintcdn.com/lightdash/ukKSe6OQutHc1e3S/images/agents/agent-identity/clickhouse-ai-service-account-setup.png?fit=max&auto=format&n=ukKSe6OQutHc1e3S&q=85&s=660734b54186a3b5ad1b2096cf47b661" alt="The shared agent account card on a ClickHouse project, marked Not in use, with an Add shared agent account button and the setup steps: the CREATE USER SQL with readonly = 2, the GRANT SELECT statement, the row policy template, and Add it here and select Test" width="996" height="860" data-path="images/agents/agent-identity/clickhouse-ai-service-account-setup.png" />
</Frame>

### Limit rows on ClickHouse

The setup steps have a row policy template. Replace the table and condition before you run it:

```sql theme={null}
CREATE ROW POLICY ai_agents_rows
  ON `<database>`.`<table>`
  FOR SELECT USING <condition>
  TO ai_agents;
```

See [Row policies](https://clickhouse.com/docs/sql-reference/statements/create/row-policy).

### Test, replace and remove

**Test** signs in and runs `SELECT currentUser(), getSetting('readonly'), getSetting('use_query_cache')`, with the same query tags and timezone as an agent query. Test fails when:

* The user is not the saved user. The match is exact, including case.
* `readonly` is 0, so the user can write.
* The query cache is on.
* ClickHouse blocks the query settings.

The card then shows "Signs in as" followed by the user, and when it was tested and added. Test does not check what the user can read. When Test fails, the card gives a fixed message: rejected credentials, blocked settings, missing grants, or a general message. Lightdash never shows the driver message.

* To change the user or password, select **Replace**.
* To remove the shared agent account, select **Remove** and confirm. Agent queries on that project are refused until an admin adds one again.

Preview projects use the shared agent account of their parent project. See [Preview projects](#preview-projects).

### What agents and people see on ClickHouse

When the ClickHouse rule is **A shared agent account**:

* Chat, [MCP](/agents/lightdash-mcp), [Slack](/integrations/slack) and data app sample queries on ClickHouse sign in as the shared agent account.
* Agent queries turn the ClickHouse query cache off.
* **Settings → My agent identity** shows a ClickHouse card. People have nothing to set up. See [My agent identity](#my-agent-identity).

When a project has no shared agent account, or ClickHouse rejects the user or password, Lightdash refuses the agent query. See [When the shared agent account is missing or fails](#when-the-shared-agent-account-is-missing-or-fails). Lightdash never falls back to the connection's own credentials.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.