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
| Property | Value |
|---|---|
| Platform | Snowflake |
| Category | Data Warehouse (Custom) |
| Setup Complexity | Medium |
| Official Integration | No |
| Data Synced | Customers, Segments, Analytics, Events |
| Auth Method | Key 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:
- A Snowflake account with ACCOUNTADMIN or SYSADMIN role
- A Brevo account with API access
- A Tajo account with connector permissions
- A dedicated Snowflake warehouse for integration queries
- Network policy allowing Tajo IP addresses
Authentication
Key Pair Authentication (Recommended)
# Generate RSA key pairopenssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocryptopenssl 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
# Using JWT token with the SQL APIcurl -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_syncField 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_NAMEAPI Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/v2/statements | POST | Submit SQL statements for execution |
/api/v2/statements/{statementHandle} | GET | Check execution status |
/api/v2/statements/{statementHandle}/cancel | POST | Cancel a running statement |
/api/v2/statements/{statementHandle}?partition={id} | GET | Retrieve 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 APIconst 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 resultslet status = result.statementStatusUrl;while (result.code !== '090001') { const check = await fetch(status, { headers: { 'Authorization': `Bearer ${jwtToken}` } }); result = await check.json();}
// Sync to Brevo via Tajofor (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 listsawait 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
| Resource | Limit | Notes |
|---|---|---|
| SQL API concurrent queries | 20 per user | Per Snowflake account |
| SQL API result size | 12MB per partition | Paginate with partition IDs |
| Statement timeout | 172,800 sec (48h) | Configurable per query |
| API requests | Varies by plan | Based 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
| Issue | Cause | Solution |
|---|---|---|
| Authentication failed | Expired JWT token | Regenerate JWT with valid expiration |
| Query timeout | Large dataset | Add filters or use incremental sync |
| Network error | IP not whitelisted | Add Tajo IPs to Snowflake network policy |
| Missing columns | Schema change | Update field mapping configuration |
| Partition error | Result too large | Process results in smaller partitions |
Debug Mode
connectors: snowflake: debug: true log_level: verbose log_queries: trueBest Practices
- Use a dedicated warehouse - Avoid contention with production workloads
- Implement incremental sync - Query only changed records since last sync
- Set auto-suspend - Configure warehouse to suspend after 5 minutes of inactivity
- Use key pair auth - Prefer key pair over password authentication
- Optimize queries - Filter and project only needed columns for faster syncs
- 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
Related Resources
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
| Repository | Commit | Languages / formats | Files |
|---|---|---|---|
| snowflakedb/snowflake-rest-api-specs | 42671a9 | YAML (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.