Snowflake Connector

Connect Snowflake to Brevo through Tajo to sync customer segments from your data warehouse, enrich contact profiles with analytics data, and power personalized marketing campaigns with warehouse-driven insights.

Overview

PropertyValue
PlatformSnowflake
CategoryData Warehouse (Custom)
Setup ComplexityMedium
Official IntegrationNo
Data SyncedCustomers, Segments, Analytics, Events
Auth MethodKey Pair / OAuth 2.0

Features

  • Reverse ETL - Push customer segments from Snowflake to Brevo contact lists
  • Audience sync - Sync warehouse-computed audiences for targeted campaigns
  • Analytics enrichment - Enrich Brevo contacts with computed metrics (LTV, RFM scores)
  • SQL-based queries - Use the Snowflake SQL REST API to execute queries programmatically
  • Scheduled sync - Run automated data pipelines on configurable intervals
  • Multi-statement support - Execute complex data transformations in single API calls

Prerequisites

Before you begin, ensure you have:

  1. A Snowflake account with ACCOUNTADMIN or SYSADMIN role
  2. A Brevo account with API access
  3. A Tajo account with connector permissions
  4. A dedicated Snowflake warehouse for integration queries
  5. Network policy allowing Tajo IP addresses

Authentication

Terminal window
# Generate RSA key pair
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
# Assign public key to Snowflake user
# In Snowflake:
# ALTER USER tajo_integration SET RSA_PUBLIC_KEY='MII...';

OAuth 2.0 Authentication

const tokenResponse = await fetch(
'https://<account>.snowflakecomputing.com/oauth/token-request',
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.SNOWFLAKE_CLIENT_ID,
client_secret: process.env.SNOWFLAKE_CLIENT_SECRET,
scope: 'session:role:TAJO_ROLE'
})
}
);

SQL API Authentication

Terminal window
# Using JWT token with the SQL API
curl -X POST \
'https://<account>.snowflakecomputing.com/api/v2/statements' \
-H 'Authorization: Bearer <jwt_token>' \
-H 'Content-Type: application/json' \
-H 'X-Snowflake-Authorization-Token-Type: KEYPAIR_JWT' \
-d '{"statement": "SELECT * FROM customers LIMIT 10", "warehouse": "TAJO_WH"}'

Configuration

Basic Setup

connectors:
snowflake:
enabled: true
account: "your-account.snowflakecomputing.com"
warehouse: "TAJO_WH"
database: "MARKETING_DB"
schema: "PUBLIC"
role: "TAJO_ROLE"
sync:
customers: true
segments: true
analytics: true
schedule: "0 */6 * * *" # Every 6 hours
queries:
customer_segments: |
SELECT email, segment_name, ltv_score, rfm_class
FROM customer_segments
WHERE updated_at > :last_sync

Field Mapping

field_mapping:
email: email
first_name: FIRSTNAME
last_name: LASTNAME
ltv_score: LTV_SCORE
rfm_class: RFM_SEGMENT
total_orders: ORDER_COUNT
last_purchase_date: LAST_ORDER_DATE
predicted_churn: CHURN_RISK
customer_segment: SEGMENT_NAME

API Endpoints

EndpointMethodDescription
/api/v2/statementsPOSTSubmit SQL statements for execution
/api/v2/statements/{statementHandle}GETCheck execution status
/api/v2/statements/{statementHandle}/cancelPOSTCancel a running statement
/api/v2/statements/{statementHandle}?partition={id}GETRetrieve result partitions

SQL API Partitions

Snowflake SQL API returns large result sets in partitions. Each partition contains up to approximately 12MB of data. Use the partition parameter to iterate through results.

Code Examples

Initialize Connector

import { TajoClient } from '@tajo/sdk';
const tajo = new TajoClient({
apiKey: process.env.TAJO_API_KEY,
brevoApiKey: process.env.BREVO_API_KEY
});
await tajo.connectors.connect('snowflake', {
account: process.env.SNOWFLAKE_ACCOUNT,
privateKey: process.env.SNOWFLAKE_PRIVATE_KEY,
warehouse: 'TAJO_WH',
database: 'MARKETING_DB',
schema: 'PUBLIC'
});

Sync Customer Segments via SQL API

// Execute a SQL query via Snowflake SQL REST API
const response = await fetch(
`https://${account}.snowflakecomputing.com/api/v2/statements`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${jwtToken}`,
'Content-Type': 'application/json',
'X-Snowflake-Authorization-Token-Type': 'KEYPAIR_JWT'
},
body: JSON.stringify({
statement: `SELECT email, segment, ltv FROM customer_segments
WHERE updated_at > '${lastSync}'`,
warehouse: 'TAJO_WH',
database: 'MARKETING_DB',
schema: 'PUBLIC',
timeout: 120
})
}
);
const result = await response.json();
const statementHandle = result.statementHandle;
// Poll for results
let status = result.statementStatusUrl;
while (result.code !== '090001') {
const check = await fetch(status, {
headers: { 'Authorization': `Bearer ${jwtToken}` }
});
result = await check.json();
}
// Sync to Brevo via Tajo
for (const row of result.data) {
await tajo.contacts.sync({
email: row[0],
attributes: { SEGMENT: row[1], LTV: row[2] }
});
}

Reverse ETL Pipeline

// Push computed audiences from Snowflake to Brevo lists
await tajo.connectors.sync('snowflake', {
type: 'reverse-etl',
query: `
SELECT email, first_name, last_name, predicted_ltv, churn_score
FROM ml_predictions.customer_scores
WHERE score_date = CURRENT_DATE()
`,
destination: {
list_id: 42,
attribute_mapping: {
predicted_ltv: 'PREDICTED_LTV',
churn_score: 'CHURN_SCORE'
}
}
});

Rate Limits

ResourceLimitNotes
SQL API concurrent queries20 per userPer Snowflake account
SQL API result size12MB per partitionPaginate with partition IDs
Statement timeout172,800 sec (48h)Configurable per query
API requestsVaries by planBased on Snowflake edition

Warehouse Costs

Snowflake charges based on compute time. Use a dedicated, appropriately-sized warehouse for Tajo queries and set auto-suspend to minimize costs.

Troubleshooting

IssueCauseSolution
Authentication failedExpired JWT tokenRegenerate JWT with valid expiration
Query timeoutLarge datasetAdd filters or use incremental sync
Network errorIP not whitelistedAdd Tajo IPs to Snowflake network policy
Missing columnsSchema changeUpdate field mapping configuration
Partition errorResult too largeProcess results in smaller partitions

Debug Mode

connectors:
snowflake:
debug: true
log_level: verbose
log_queries: true

Best Practices

  1. Use a dedicated warehouse - Avoid contention with production workloads
  2. Implement incremental sync - Query only changed records since last sync
  3. Set auto-suspend - Configure warehouse to suspend after 5 minutes of inactivity
  4. Use key pair auth - Prefer key pair over password authentication
  5. Optimize queries - Filter and project only needed columns for faster syncs
  6. Monitor credits - Track Snowflake credit consumption for integration queries

Security

  • Key pair authentication - RSA 2048-bit encryption for API access
  • OAuth 2.0 - Token-based authentication with role scoping
  • Network policies - IP allowlisting for Tajo service endpoints
  • Role-based access - Dedicated Snowflake role with minimum required privileges
  • Encrypted data transfer - TLS 1.2+ for all API communications
  • Data masking - Use Snowflake dynamic data masking for sensitive fields

Open-Source Implementation Map

This section is derived from official or public repository material discovered for the Snowflake connector. Use it as the engineering companion to the setup guide above: it shows where the API surface lives, what implementation assets exist, and how Tajo should translate them into reliable Brevo sync behavior.

Repository Snapshot

RepositoryCommitLanguages / formatsFiles
snowflakedb/snowflake-rest-api-specs42671a9YAML (53), JSON (47), Markdown (2), YAML (2), github/codeowners (1), gitignore (1)107

Integration Shape

graph LR
Source["Snowflake API / repository"] --> Auth["Auth and scopes"]
Source --> Objects["Objects, events, and schemas"]
Auth --> Tajo["Tajo connector runtime"]
Objects --> Tajo
Tajo --> Brevo["Brevo contacts, attributes, lists, campaigns"]
Tajo --> Ops["Backfill, cursor, retries, logs"]

What To Reuse

  • README summary was not available from the captured repository files.

Tajo Revamp Checklist

  • Keep authentication setup aligned with the vendor docs and the public repository’s current API shape.
  • Map primary resources into explicit Tajo sync objects with stable external IDs.
  • Prefer cursor-based or updated-at incremental sync where the API exposes it; otherwise document the fallback.
  • Treat webhook handlers as idempotent and replay-safe, especially for order, contact, ticket, and campaign events.
  • Capture pagination, rate limits, retry headers, and partial-failure behavior in connector smoke tests.
  • Keep examples small and runnable against sandbox or test-mode accounts.

Sources

Subscribe to updates

developer-docs

Drop your email or phone number — we'll send you what matters next.

auto-detect
AI Assistant

Hi! Ask me anything about the docs.