Connectors
/
Snowflake
Live · 14 tools

Snowflake Integration for AI Agents

The Snowflake SQL API is documented. Getting your agent to query it correctly — for real users, with OAuth tokens that survive role switches, warehouse context, and multi-tenant isolation — is the part that takes longer than it should.
Snowflake
Live

Data Warehouse

Analytics

Status
Live
Tools
14 pre-built
Auth
OAuth 2.0
Credential storage
Sandbox support

OAuth 2.0

Auto token refresh

Revocation detection

BYOC required

The real problem

Why this is harder than it looks

Snowflake's SQL API is clean and well-structured. The OAuth 2.0 flow is standard. Most developers figure the auth layer will take a day. Then they hit what the documentation buries in footnotes.

Snowflake OAuth requires you to create an OAuth Security Integration inside your Snowflake account using SQL — there's no UI-driven app registration. Each customer's Snowflake instance is its own account with a unique domain (orgname-accountname.snowflakecomputing.com), and the OAuth endpoints and API base URLs are derived from that account locator. That means you can't hardcode a base URL. Every user in your product is connecting to a different Snowflake account, and your code must resolve the correct endpoint per user at runtime. Scalekit handles this: the account domain is captured during the OAuth flow and applied automatically on every subsequent API call.

Then there's the role and warehouse context problem. Snowflake's SQL API requires an active warehouse to execute queries — if none is specified and the user's default warehouse is suspended or unset, queries fail silently or return an unhelpful error. Roles in Snowflake are additive and context-sensitive: a user may have multiple roles with different data access, and the role active at query time determines what they can see. Getting this wrong means queries that work in development fail in production when a different user with a different default role connects.

On top of that: per-user token isolation across tenants, proactive refresh before expiry, revocation detection when a user's Snowflake session is revoked by an admin, and the BYOC requirement that means you must create and manage an OAuth Security Integration in every customer's Snowflake account. Scalekit handles the token lifecycle end-to-end. Your agent names a tool and passes SQL.

Capabilities

What your agent can do with Snowflake

Once connected, your agent has 14 pre-built tools covering query execution, schema introspection, and warehouse management:

  • Execute SQL against any database: run queries synchronously or asynchronously, with role, warehouse, database, and schema context per call
  • Inspect schemas and tables: browse databases, schemas, tables, and column metadata via INFORMATION_SCHEMA — without writing boilerplate SQL
  • Track async query status: submit long-running queries asynchronously, poll status by handle, and retrieve paginated result partitions
  • Understand data relationships: query table constraints, primary keys, referential constraints, and imported/exported key relationships
  • Audit access and capacity: show grants, list warehouses, and inspect role-level permissions programmatically
Setup context

What we're building

This guide connects a data assistant agent to Snowflake — helping analysts and engineers query warehouse data, explore schemas, and run SQL without leaving your product.

🤖
Example agent
Data assistant executing SQL and exploring schemas on behalf of each analyst
🔐
Auth model
B2B SaaS — each user connects their own Snowflake account. identifier = your user ID
⚙️
Scalekit account
app.scalekit.com — Client ID, Secret, Env URL
🐍
Runtime
Python 3.8+ · Node.js also available
Setup

1 Setup: One SDK, One credential

Install the Scalekit SDK. The only credential your application manages is the Scalekit API key — no Snowflake secrets, no user tokens, nothing belonging to your customers.

pip install scalekit-sdk-python
npm install @scalekit-sdk/node
import scalekit.client import os from dotenv import load_dotenv load_dotenv() scalekit = scalekit.client.ScalekitClient( client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), env_url=os.getenv("SCALEKIT_ENV_URL"), ) actions = scalekit.actions
import { ScalekitClient } from '@scalekit-sdk/node'; import 'dotenv/config'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENV_URL, process.env.SCALEKIT_CLIENT_ID, process.env.SCALEKIT_CLIENT_SECRET ); const actions = scalekit.actions;
Already have credentials?
Connected Accounts

2 Per-User Auth: Creating connected accounts

Each analyst gets their own Connected Account, giving them a dedicated auth context. The identifier is any unique string from your system — a UUID, email, whatever you use internally.

response = actions.get_or_create_connected_account( connection_name="snowflake", identifier="user_sf_456" # your internal user ID ) connected_account = response.connected_account print(f"Status: {connected_account.status}") # Status: PENDING — user hasn't authorized yet
const response = await actions.getOrCreateConnectedAccount({ connectionName: "snowflake", identifier: "user_sf_456" // your internal user ID }); const connectedAccount = response.connectedAccount; console.log(`Status: ${connectedAccount.status}`); // Status: PENDING — user hasn't authorized yet

This call is idempotent — safe to call on every session start. Returns the existing account if one already exists.

Authorization Flow

3 The authorization flow

The user authorizes your agent once. Scalekit generates the OAuth URL with correct scopes, PKCE challenge, and redirect handling pre-configured. After approval, you never see the token.

if connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name="snowflake", identifier="user_sf_456" ) # Redirect user → Snowflake OAuth consent screen # Scalekit captures the token and account domain on callback return redirect(link.link)
if (connectedAccount.status !== "ACTIVE") { const { link } = await actions.getAuthorizationLink({ connectionName: "snowflake", identifier: "user_sf_456" }); // Redirect user → Snowflake OAuth consent screen // Scalekit captures the token and account domain on callback return redirect(link); }
Token management is automatic
After the user approves, Scalekit stores encrypted tokens and the connected account moves to ACTIVE. Access tokens refresh before expiry. If a user's token is revoked by a Snowflake admin, the account moves to REVOKED — no silent failures. Check account.status before critical operations.
BYOC is required for Snowflake
Snowflake requires you to create an OAuth Security Integration in your own Snowflake account using SQL. Run the CREATE SECURITY INTEGRATION statement in Snowsight, retrieve the Client ID and Secret via SYSTEM$SHOW_OAUTH_CLIENT_SECRETS, then paste them into the Scalekit dashboard. Token management is fully handled after that one-time setup.
Calling Snowflake

4 Calling Snowflake: What your agent writes

With the connected account active, your agent calls Snowflake actions via execute_tool. Name the tool, pass parameters. Scalekit handles token retrieval, account domain resolution, and request construction.

Execute a SQL query

Run any SQL statement against a specified database and schema. Pass warehouse and role per call to override the user's defaults — useful when your agent needs to query across different contexts.

result = actions.execute_tool( identifier="user_sf_456", tool_name="snowflake_execute_query", tool_input={ "statement": "SELECT order_id, customer_id, total_amount, status FROM orders WHERE status = 'PENDING' ORDER BY total_amount DESC LIMIT 20", "database": "ANALYTICS", "schema": "PUBLIC", "warehouse": "COMPUTE_WH", "role": "ANALYST" } ) # Returns: { "statementHandle": "...", "data": [[...], ...], "resultSetMetaData": {...} }
const result = await actions.executeTool({ identifier: "user_sf_456", toolName: "snowflake_execute_query", toolInput: { statement: "SELECT order_id, customer_id, total_amount, status FROM orders WHERE status = 'PENDING' ORDER BY total_amount DESC LIMIT 20", database: "ANALYTICS", schema: "PUBLIC", warehouse: "COMPUTE_WH", role: "ANALYST" } }); // Returns: { statementHandle: "...", data: [[...], ...], resultSetMetaData: {...} }

Explore schema and tables

Enumerate available schemas and tables before running queries — useful for agents that need to discover what data exists in a database before constructing SQL.

# List schemas in a database schemata = actions.execute_tool( identifier="user_sf_456", tool_name="snowflake_get_schemata", tool_input={ "database": "ANALYTICS", "warehouse": "COMPUTE_WH" } ) # List tables in a schema tables = actions.execute_tool( identifier="user_sf_456", tool_name="snowflake_get_tables", tool_input={ "database": "ANALYTICS", "schema": "PUBLIC", "warehouse": "COMPUTE_WH", "limit": 50 } ) # Returns table names, types, row counts, and creation timestamps
// List schemas in a database const schemata = await actions.executeTool({ identifier: "user_sf_456", toolName: "snowflake_get_schemata", toolInput: { database: "ANALYTICS", warehouse: "COMPUTE_WH" } }); // List tables in a schema const tables = await actions.executeTool({ identifier: "user_sf_456", toolName: "snowflake_get_tables", toolInput: { database: "ANALYTICS", schema: "PUBLIC", warehouse: "COMPUTE_WH", limit: 50 } }); // Returns table names, types, row counts, and creation timestamps

Inspect column metadata

Fetch column definitions for a specific table — data types, nullability, and defaults. Useful for agents that generate SQL dynamically and need to know the schema before constructing a query.

columns = actions.execute_tool( identifier="user_sf_456", tool_name="snowflake_get_columns", tool_input={ "database": "ANALYTICS", "schema": "PUBLIC", "table": "ORDERS", "warehouse": "COMPUTE_WH" } ) # Returns: column names, data types, is_nullable, column_default
const columns = await actions.executeTool({ identifier: "user_sf_456", toolName: "snowflake_get_columns", toolInput: { database: "ANALYTICS", schema: "PUBLIC", table: "ORDERS", warehouse: "COMPUTE_WH" } }); // Returns: column names, data types, is_nullable, column_default

Run async queries and poll results

For long-running queries, submit asynchronously and poll by handle. Use snowflake_get_query_partition to page through large result sets without loading everything at once.

import time # Submit async submit = actions.execute_tool( identifier="user_sf_456", tool_name="snowflake_execute_query", tool_input={ "statement": "SELECT * FROM large_events_table WHERE event_date >= '2026-01-01'", "database": "ANALYTICS", "schema": "PUBLIC", "warehouse": "COMPUTE_WH", "async": True } ) handle = submit["statementHandle"] # Poll until complete while True: status = actions.execute_tool( identifier="user_sf_456", tool_name="snowflake_get_query_status", tool_input={"statement_handle": handle} ) if status["status"] == "success": break time.sleep(2) # Fetch first partition results = actions.execute_tool( identifier="user_sf_456", tool_name="snowflake_get_query_partition", tool_input={"statement_handle": handle, "partition": 0} )
// Submit async const submit = await actions.executeTool({ identifier: "user_sf_456", toolName: "snowflake_execute_query", toolInput: { statement: "SELECT * FROM large_events_table WHERE event_date >= '2026-01-01'", database: "ANALYTICS", schema: "PUBLIC", warehouse: "COMPUTE_WH", async: true } }); const handle = submit.statementHandle; // Poll until complete let status; do { await new Promise(r => setTimeout(r, 2000)); status = await actions.executeTool({ identifier: "user_sf_456", toolName: "snowflake_get_query_status", toolInput: { statement_handle: handle } }); } while (status.status !== "success"); // Fetch first partition const results = await actions.executeTool({ identifier: "user_sf_456", toolName: "snowflake_get_query_partition", toolInput: { statement_handle: handle, partition: 0 } });
Framework wiring

5 Wiring into your agent framework

Scalekit integrates directly with LangChain. The agent decides what to query; Scalekit handles auth on every invocation. No token plumbing in your agent logic.

from langchain_anthropic import ChatAnthropic from langchain.agents import AgentExecutor, create_tool_calling_agent from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from scalekit.langchain import get_tools sf_tools = get_tools( connection_name="snowflake", identifier="user_sf_456" ) prompt = ChatPromptTemplate.from_messages([ ("system", "You are a data assistant. Use the available tools to help query and explore Snowflake data warehouses."), MessagesPlaceholder("chat_history", optional=True), ("human", "{input}"), MessagesPlaceholder("agent_scratchpad"), ]) agent = create_tool_calling_agent(ChatAnthropic(model="claude-sonnet-4-6"), sf_tools, prompt) result = AgentExecutor(agent=agent, tools=sf_tools).invoke({ "input": "Show me the top 10 customers by revenue this quarter from the ANALYTICS database" })
import { ChatAnthropic } from "@langchain/anthropic"; import { AgentExecutor, createToolCallingAgent } from "langchain/agents"; import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts"; import { getTools } from "@scalekit-sdk/langchain"; const sfTools = getTools({ connectionName: "snowflake", identifier: "user_sf_456" }); const prompt = ChatPromptTemplate.fromMessages([ ["system", "You are a data assistant. Use the available tools to help query and explore Snowflake data warehouses."], new MessagesPlaceholder("chat_history", true), ["human", "{input}"], new MessagesPlaceholder("agent_scratchpad"), ]); const agent = await createToolCallingAgent({ llm: new ChatAnthropic({ model: "claude-sonnet-4-6" }), tools: sfTools, prompt }); const result = await AgentExecutor.fromAgentAndTools({ agent, tools: sfTools }).invoke({ input: "Show me the top 10 customers by revenue this quarter from the ANALYTICS database" });
Other frameworks supported
Tool reference

All 14 Snowflake tools

Grouped by capability. Your agent calls tools by name — no API wrappers to write.

Query Execution
snowflake_execute_query
Execute one or more SQL statements. Supports sync and async modes, role/warehouse/database context, bind variables, and idempotent retries via request_id
snowflake_cancel_query
Cancel a running statement by its statement handle
snowflake_get_query_status
Get the execution status and first partition metadata for an async statement by handle
snowflake_get_query_partition
Retrieve a specific result partition (0-based index) for a completed async statement
Schema Introspection
snowflake_show_databases_schemas
Run SHOW DATABASES or SHOW SCHEMAS with optional LIKE pattern and database scope
snowflake_get_schemata
Query INFORMATION_SCHEMA.SCHEMATA for schema metadata within a database
snowflake_get_tables
Query INFORMATION_SCHEMA.TABLES with optional schema filter, table name pattern, and row limit
snowflake_get_columns
Query INFORMATION_SCHEMA.COLUMNS for column names, data types, nullability, and defaults — with optional table, schema, and column name filters
Constraints & Keys
snowflake_get_table_constraints
Query INFORMATION_SCHEMA.TABLE_CONSTRAINTS with optional constraint type, schema, and table filters
snowflake_get_referential_constraints
Query INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS for foreign key relationships between tables
snowflake_show_primary_keys
Run SHOW PRIMARY KEYS with optional database, schema, and table scope
snowflake_show_imported_exported_keys
Run SHOW IMPORTED KEYS or SHOW EXPORTED KEYS for a table. Use fully-qualified scope (database + schema + table) for reliable execution
Access & Warehouses
snowflake_show_warehouses
Run SHOW WAREHOUSES to list available compute warehouses with optional LIKE pattern filter
snowflake_show_grants
Run SHOW GRANTS in common modes — to role, to user, of role, or on a specific object — to inspect role-level permissions
Connector notes

Snowflake-specific behavior

BYOC is required — no managed app available
Snowflake does not have a central app registry. You must create an OAuth Security Integration inside your own Snowflake account using SQL (CREATE SECURITY INTEGRATION), retrieve the Client ID and Secret via SYSTEM$SHOW_OAUTH_CLIENT_SECRETS, and paste them into the Scalekit dashboard. Each customer's Snowflake account is a separate OAuth registration.
Always specify warehouse — absent defaults cause silent failures
Snowflake requires an active virtual warehouse to execute queries. If the user's default warehouse is suspended or unset, queries fail with an ambiguous error. Pass warehouse explicitly on every snowflake_execute_query call rather than relying on session defaults.
Account domain is resolved automatically
Every Snowflake account has a unique domain (orgname-accountname.snowflakecomputing.com). Scalekit captures this during the OAuth flow and routes all subsequent API calls to the correct endpoint automatically. You do not need to store or pass the account locator in your application code.
Infrastructure decision

Why not build this yourself

The Snowflake OAuth flow is documented. Token storage isn't technically hard. But here's what you're actually signing up for:

PROBLEM 01
Each customer's Snowflake account has a unique domain — you must resolve and store the correct API endpoint per user, and route every call to the right account at runtime
PROBLEM 02
BYOC is mandatory — OAuth Security Integrations must be created via SQL inside each Snowflake account, there's no central app registry or UI-driven setup path
PROBLEM 03
Per-user token isolation across a multi-tenant system — one analyst's Snowflake credentials must never be accessible to another, even within the same organization
PROBLEM 04
Revocation detection when a Snowflake admin revokes a user's OAuth token — and graceful handling so the agent stops making calls and surfaces a re-authorization prompt

That's one connector. Your agent product will eventually need Salesforce, Gmail, HubSpot, Notion, and whatever else your customers ask for. Each has its own OAuth quirks and failure modes.

Scalekit maintains every connector. You maintain none of them.

Ready to ship

Connect your agent to Snowflake now

Free to start. Token management fully handled.
Snowflake
Live

Data Warehouse

Analytics

Status
Live
Tools
14 pre-built
Auth
OAuth 2.0
Credential storage
Sandbox support