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

# Snowflake

> Connect Summand to a Snowflake account for live analysis. Supports key-pair JWT, programmatic access tokens, and password authentication.

The Snowflake connector talks to your Snowflake account over TLS using the official `snowflake-sdk` (Node.js) and `snowflake-connector-python` libraries. Available on **Enterprise** plans.

Summand follows Snowflake's documented best practices for service connectors: dedicated read-only role, modern authentication (key-pair JWT or programmatic access tokens), structured query tags for cost attribution, and a 1-hour statement timeout to bound runaway queries.

## What Summand needs

A dedicated Snowflake **role** with read-only access to the database, schema, and tables you intend to analyze. Snowflake's [access control best practices](https://docs.snowflake.com/en/user-guide/security-access-control-considerations) recommend creating an integration-specific role rather than reusing a human user's role.

The role needs:

* `USAGE` on the **warehouse**
* `USAGE` on the **database** and **schemas**
* `SELECT` on the **tables and views** to be analyzed (or `SELECT ON ALL TABLES IN SCHEMA …` plus `SELECT ON FUTURE TABLES IN SCHEMA …` for new tables)

Summand never executes writes, never creates Snowflake objects, and never runs user-supplied SQL against your warehouse.

## Authentication

Snowflake is [deprecating single-factor password authentication for service users by October 2026](https://docs.snowflake.com/en/user-guide/security-mfa-rollout). Summand supports three auth methods, listed in order of preference:

| Method                              | Recommended for                             | Snowflake's stance                         |
| ----------------------------------- | ------------------------------------------- | ------------------------------------------ |
| **Key-pair (JWT)**                  | Production, all customers                   | ✅ Required for service users post-Oct 2026 |
| **Programmatic Access Token (PAT)** | Quick setup, rotation-friendly service auth | ✅ Supported (introduced 2024)              |
| **Password**                        | Initial trial only                          | ⚠️ Being deprecated for service users      |

### Key-pair JWT (recommended)

<Steps>
  <Step title="Generate a key pair">
    On any machine with `openssl`:

    ```bash theme={null}
    # Generate an encrypted PKCS#8 private key
    openssl genrsa 2048 | openssl pkcs8 -topk8 -v2 des3 -inform PEM -out summand_rsa_key.p8

    # Extract the matching public key
    openssl rsa -in summand_rsa_key.p8 -pubout -out summand_rsa_key.pub
    ```

    The private key (`summand_rsa_key.p8`) goes into Summand. The public key (`summand_rsa_key.pub`) goes into Snowflake.
  </Step>

  <Step title="Register the public key on the Snowflake user">
    Strip the PEM header/footer from `summand_rsa_key.pub` and run, in Snowflake:

    ```sql theme={null}
    ALTER USER SUMMAND_READER SET RSA_PUBLIC_KEY = '<key contents without BEGIN/END lines>';
    ```

    Snowflake's [key rotation guide](https://docs.snowflake.com/en/user-guide/key-pair-auth) explains how to rotate via `RSA_PUBLIC_KEY_2` without downtime.
  </Step>

  <Step title="Add the connector in Summand">
    In **Connectors → Add connector → Snowflake**:

    * Choose **Key pair (recommended)**
    * Paste the contents of `summand_rsa_key.p8` into **Private key**
    * If you used `-v2 des3` (encrypted), supply the **Passphrase**
  </Step>
</Steps>

### Programmatic Access Token (PAT)

[Introduced in 2024](https://docs.snowflake.com/en/user-guide/programmatic-access-tokens), PATs are token-based credentials scoped to a single user — simpler than key-pair, more secure than passwords. Best for "OAuth without the redirect dance."

<Steps>
  <Step title="Generate a PAT in Snowflake">
    ```sql theme={null}
    -- As an admin or the user themselves
    ALTER USER SUMMAND_READER ADD PROGRAMMATIC ACCESS TOKEN summand_token
      DAYS_TO_EXPIRY = 90
      ROLE_RESTRICTION = 'SUMMAND_READER';
    ```

    Snowflake returns the token value **once** — copy it immediately. Tokens are revocable via `ALTER USER … REMOVE PROGRAMMATIC ACCESS TOKEN summand_token`.
  </Step>

  <Step title="Add the connector">
    In **Connectors → Add connector → Snowflake**:

    * Choose **Programmatic Access Token**
    * Paste the token value into the **Token** field
  </Step>
</Steps>

### Password (legacy)

Supported for backward compatibility but [Snowflake is enforcing key-pair / OAuth for service users by October 2026](https://docs.snowflake.com/en/user-guide/security-mfa-rollout). Existing password-auth connectors should be migrated before then; new connectors should default to key-pair or PAT.

## Setup script — least-privilege role

Run this in Snowflake (as `SECURITYADMIN` or `SYSADMIN`) before adding the connector. Adjust `<DB>`, `<SCHEMA>`, and `<WAREHOUSE>` to your environment:

```sql theme={null}
USE ROLE SECURITYADMIN;

-- Dedicated user + role for Summand
CREATE ROLE IF NOT EXISTS SUMMAND_READER;
CREATE USER IF NOT EXISTS SUMMAND_READER
  DEFAULT_ROLE = SUMMAND_READER
  DEFAULT_WAREHOUSE = <WAREHOUSE>
  COMMENT = 'Read-only role for the Summand integration';
GRANT ROLE SUMMAND_READER TO USER SUMMAND_READER;

-- Allow the role to use the warehouse
USE ROLE SYSADMIN;
GRANT USAGE ON WAREHOUSE <WAREHOUSE> TO ROLE SUMMAND_READER;

-- Grant database + schema USAGE and SELECT on tables/views
GRANT USAGE ON DATABASE <DB> TO ROLE SUMMAND_READER;
GRANT USAGE ON SCHEMA <DB>.<SCHEMA> TO ROLE SUMMAND_READER;
GRANT SELECT ON ALL TABLES IN SCHEMA <DB>.<SCHEMA> TO ROLE SUMMAND_READER;
GRANT SELECT ON ALL VIEWS IN SCHEMA <DB>.<SCHEMA> TO ROLE SUMMAND_READER;

-- Auto-grant SELECT on future objects (so adding tables/views doesn't require re-running grants)
GRANT SELECT ON FUTURE TABLES IN SCHEMA <DB>.<SCHEMA> TO ROLE SUMMAND_READER;
GRANT SELECT ON FUTURE VIEWS IN SCHEMA <DB>.<SCHEMA> TO ROLE SUMMAND_READER;
```

For multi-schema access, repeat the schema-level grants for each schema, or grant at the database level if you want the role to see every schema.

## Network access

By default Summand connects from a fixed pool of egress NAT IPs (visible in **Settings → Connectivity**). Three deployment options exist:

### 1. Public endpoint with IP allowlist (default)

Add Summand's egress IPs to your Snowflake [network policy](https://docs.snowflake.com/en/user-guide/network-policies):

```sql theme={null}
USE ROLE SECURITYADMIN;
CREATE NETWORK POLICY summand_allowlist
  ALLOWED_IP_LIST = ('<summand-egress-ip-1>', '<summand-egress-ip-2>', ...)
  COMMENT = 'Allow Summand connector traffic';

-- Apply at the user level (recommended) or account level
ALTER USER SUMMAND_READER SET NETWORK_POLICY = 'summand_allowlist';
```

### 2. AWS PrivateLink (Enterprise only)

For accounts that disable public traffic via [AWS PrivateLink](https://docs.snowflake.com/en/user-guide/admin-security-privatelink), Summand can be added to your VPC peering. Contact [sales@summand.com](mailto:sales@summand.com) — this is a managed-peering setup and requires both sides to coordinate.

### 3. Hostname allowlist on customer side

If your firewall blocks outbound by hostname, allow:

* `<account>.snowflakecomputing.com` (account-specific URL)
* `<account>.<region>.privatelink.snowflakecomputing.com` (PrivateLink endpoint, if applicable)
* `*.snowflakecomputing.com` if you prefer broad allowlisting

## Adding the connector

From **Connectors → Add connector → Snowflake**:

| Field                             | Value                                                                                                                                                                                                  |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Account**                       | The `<org>-<account>` identifier (e.g. `xy12345-MYACCT`). Summand auto-strips a trailing `.snowflakecomputing.com` and converts `_` → `-` so you can paste the URL directly.                           |
| **Warehouse** *(optional)*        | The Snowflake warehouse the role has `USAGE` on. Leave blank to use the user's `DEFAULT_WAREHOUSE` per Snowflake's [recommended pattern](https://docs.snowflake.com/en/user-guide/warehouses-overview) |
| **Database**                      | The database to analyze                                                                                                                                                                                |
| **Username**                      | The Snowflake user (e.g. `SUMMAND_READER`)                                                                                                                                                             |
| **Authentication**                | Pick **Key pair**, **Programmatic Access Token**, or **Password**                                                                                                                                      |
| **Role** *(advanced, optional)*   | Override the user's default role — pin to `SUMMAND_READER` if you have multiple roles                                                                                                                  |
| **Schema** *(advanced, optional)* | Default schema for the session. Leave blank to discover **all schemas** the role can read                                                                                                              |

## What Summand sets on every Snowflake session

For predictable cost and operational behavior, Summand applies these session parameters automatically:

| Parameter                      | Value                                              | Why                                                                                                                                                                                       |
| ------------------------------ | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `application`                  | `Summand_Connector`                                | Identifies our queries in your audit logs and Snowflake support tickets                                                                                                                   |
| `QUERY_TAG`                    | JSON `{service, action, connector_id, request_id}` | Lets your FinOps team attribute Snowflake credits via `ACCOUNT_USAGE.QUERY_HISTORY` — see [Snowflake's cost-attribution guide](https://docs.snowflake.com/en/user-guide/cost-attributing) |
| `STATEMENT_TIMEOUT_IN_SECONDS` | `3600` (1 hour)                                    | Bounds runaway queries — the Snowflake account default is 172800 (2 days)                                                                                                                 |
| `TIMESTAMP_TYPE_MAPPING`       | `TIMESTAMP_NTZ`                                    | Deterministic timezone handling across ingestion runs                                                                                                                                     |
| `CLIENT_SESSION_KEEP_ALIVE`    | `false`                                            | Short-lived ingestion sessions don't pin warehouse credits                                                                                                                                |

## Cost attribution

Every Summand query carries a JSON `QUERY_TAG`. To see credits attributable to Summand:

```sql theme={null}
SELECT
  PARSE_JSON(QUERY_TAG):action::STRING AS summand_action,
  PARSE_JSON(QUERY_TAG):connector_id::STRING AS connector_id,
  COUNT(*) AS query_count,
  SUM(CREDITS_USED_CLOUD_SERVICES) AS credits
FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY
WHERE START_TIME > DATEADD(day, -30, CURRENT_TIMESTAMP())
  AND PARSE_JSON(QUERY_TAG):service::STRING = 'summand'
GROUP BY 1, 2
ORDER BY credits DESC;
```

## Picking tables, views, and datasets

Summand discovers **base tables, views, and materialized views** in the schema(s) you have access to. View support means dbt-style "raw tables → curated views" pipelines work out of the box — point Summand at the curated view and analyze it directly.

Same UX as any other database connector: toggle on the tables/views to analyze, set targets per dataset, run.

## Common errors

### "Snowflake view metadata is stale"

Snowflake views can drift when an underlying table gains columns and the view's stored column shape becomes out of sync. The fix must be in Snowflake (`ALTER VIEW` cannot do this):

```sql theme={null}
CREATE OR REPLACE VIEW <db>.<schema>.<view> AS <original SELECT>;
```

Summand surfaces this error with the exact view name and remediation SQL inline.

### "Connection succeeded but no warehouse is bound"

The role lacks `USAGE` on the warehouse. Run:

```sql theme={null}
GRANT USAGE ON WAREHOUSE <WAREHOUSE> TO ROLE <YOUR_ROLE>;
```

### "Account identifier not found"

Most often caused by:

* Using the legacy `<locator>.<region>.<cloud>` form — switch to `<org>-<account>`
* Trailing `.snowflakecomputing.com` (Summand auto-strips this, but other tools may not)
* Underscores in the identifier — Snowflake's Python connector requires hyphens (Summand auto-converts on the way in)
