---
title: "Snowflake Connector"
description: "Complete Snowflake integration guide for syncing data warehouse analytics, customer segments, and business intelligence with Brevo through Tajo."
url: https://tajo.io/docs/connectors/custom/snowflake/
markdown: https://tajo.io/docs/connectors/custom/snowflake.md
lang: en-US
robots: noindex
---

# Snowflake Connector

Connector status: Planned Not built yet. Tajo does not run this connector today.

| Source field | Destination field | Sample value |
| --- | --- | --- |
| `record.id` | `source_id` | `record_1042` |
| `record.updated_at` | `updated_at` | `2026-10-18T08:00:00Z` |
| `record.value` | `value` | `sample_value` |

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:

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

### Key Pair Authentication (Recommended)

```bash
# 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

```javascript
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

```bash
# 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

```yaml
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

```yaml
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

| 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

```javascript
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

```javascript
// 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

```javascript
// 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

| 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

```yaml
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

## Related Resources

- [Segment Connector](https://tajo.io/docs/connectors/custom/segment)
- [PostHog Connector](https://tajo.io/docs/connectors/custom/posthog)
- [Customer Sync Skill](https://tajo.io/docs/skills/data-sync/customer-sync)
- [Snowflake SQL API Documentation](https://docs.snowflake.com/en/developer-guide/sql-api/guide)

## 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](https://github.com/snowflakedb/snowflake-rest-api-specs) | 42671a9 | YAML (53), JSON (47), Markdown (2), YAML (2), github/codeowners (1), gitignore (1) | 107 |

### Integration Shape

```mermaid
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

- [snowflakedb/snowflake-rest-api-specs](https://github.com/snowflakedb/snowflake-rest-api-specs)
- [Vendor documentation](https://docs.snowflake.com/en/developer-guide/snowflake-rest-api/snowflake-rest-api)
