Skip to main content
This guide walks you through connecting Snowflake to Conversion and setting up your first sync.

Before You Begin

Before connecting Snowflake, we recommend the following:
  • Create a dedicated user and role — Set up a Snowflake user specifically for Conversion with read-only access to the tables you need. This limits exposure and makes it easy to revoke access if needed.
  • Use a separate warehouse — Consider creating a dedicated virtual warehouse for Conversion queries to prevent sync operations from impacting other workloads.
  • Sync only the data you need — Select only the columns you’ll use, and always filter with last_sync_time to limit results to changed rows.
Queries that return millions of rows should not run more frequently than once per day. Large, frequent syncs can impact both your Snowflake costs and Conversion performance.

Step 1: Set Up Snowflake Access

Conversion connects to Snowflake using key-pair authentication. You’ll need to create a warehouse, role, user, and generate a private key. Run the following commands in a Snowflake worksheet. We recommend using the ACCOUNTADMIN role.

Create a Virtual Warehouse

Conversion needs a virtual warehouse to run queries. You can create a dedicated one or reuse an existing warehouse.
-- Create a dedicated warehouse (optional - you can reuse an existing one)
CREATE WAREHOUSE conversion_sync
  WITH WAREHOUSE_SIZE = 'XSMALL'
  WAREHOUSE_TYPE = 'STANDARD'
  AUTO_SUSPEND = 300  -- 5 minutes
  AUTO_RESUME = TRUE;

Create a Role and Grant Access

Create a role for Conversion with read access to the tables you want to sync.
-- Create role
CREATE ROLE conversion_sync_role;

-- Grant warehouse access
GRANT USAGE ON WAREHOUSE conversion_sync TO ROLE conversion_sync_role;

-- Grant database and schema access
-- Replace with your database, schema, and table names
GRANT USAGE ON DATABASE my_database TO ROLE conversion_sync_role;
GRANT USAGE ON SCHEMA my_database.my_schema TO ROLE conversion_sync_role;
GRANT SELECT ON TABLE my_database.my_schema.users TO ROLE conversion_sync_role;
GRANT SELECT ON TABLE my_database.my_schema.products TO ROLE conversion_sync_role;
-- Add more tables as needed

Create a User

Create a user for Conversion and assign the role you created.
-- Create user
CREATE USER conversion_sync_user
  DEFAULT_ROLE = conversion_sync_role
  DEFAULT_WAREHOUSE = conversion_sync;

-- Assign role to user
GRANT ROLE conversion_sync_role TO USER conversion_sync_user;

Generate a Private Key

Conversion authenticates using a private key in PKCS#8 PEM format. Generate one using OpenSSL:
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8 -nocrypt
This creates an unencrypted private key file called rsa_key.p8. Then generate the public key and assign it to your Snowflake user:
openssl rsa -in rsa_key.p8 -pubout -out rsa_key.pub
Copy the public key (without the header/footer lines) and assign it to your user in Snowflake:
ALTER USER conversion_sync_user SET RSA_PUBLIC_KEY='MIIBIjANBgkqh...';
Keep your private key secure. You’ll paste it into Conversion when setting up the connection.

Step 2: Allow Conversion’s IP Addresses

If your Snowflake account uses network policies, allow connections from Conversion’s IP addresses:
RegionIP Addresses
US35.239.90.161, 35.188.167.166, 34.56.101.43, 34.122.97.230, 34.29.176.66, 35.226.154.44

Step 3: Connect Snowflake to Conversion

  1. In Conversion, go to Settings → Integrations → Data Warehouse
  2. Click Add Integration and select Snowflake
  3. Enter your connection details:
    • Name — A friendly name for this connection (e.g., “Production Snowflake”)
    • Account — Your Snowflake account identifier (e.g., xy12345.us-east-1)
    • Warehouse — The warehouse to use for queries
    • Database — The default database
    • Schema — The default schema
    • User — The username you created
    • Role — The role you created
    • Private Key — Paste the contents of your rsa_key.p8 file
  4. Click Connect
Conversion will verify the connection. If successful, you’re ready to create syncs.

Step 4: Create a Sync

After connecting, create a sync to start pulling data:
  1. Click Add Sync
  2. Select the destination type:
    • Contacts — Create or update contact records
    • Custom Objects — Create or update custom object records (with optional relationships)
    • Events — Record events on contacts
  3. For Custom Objects, select the specific object type
  4. Enter your SQL query (see examples below)
  5. Click Run Query to preview results
  6. Set the sync schedule (how often to run)
  7. Click Enable

Writing Queries for Snowflake

Converting Column Names to Lowercase

Snowflake returns column names in uppercase by default. Use AS "lowercase" with double quotes to ensure column names match what Conversion expects:
SELECT 
  EMAIL AS "email",
  FIRST_NAME AS "first_name",
  LAST_NAME AS "last_name"
FROM users
WHERE UPDATED_AT >= {{last_sync_time}}
Column names are case-sensitive in your results. Without AS "email", the column would be named EMAIL, which won’t match Conversion’s expected email field.

Contact Sync

SELECT 
  EMAIL AS "email",
  FIRST_NAME AS "first_name",
  LAST_NAME AS "last_name",
  TITLE AS "job_title",
  PHONE AS "phone",
  CITY AS "city"
FROM users
WHERE UPDATED_AT >= {{last_sync_time}}

Custom Objects (Without Relationships)

To sync objects without relating them to contacts, omit the email column:
SELECT 
  ID AS "id",
  NAME AS "name",
  STATUS AS "status",
  PLAN_TIER AS "plan_tier",
  MONTHLY_PRICE AS "monthly_price"
FROM subscriptions
WHERE UPDATED_AT >= {{last_sync_time}}

Custom Objects with Relationships

To sync custom objects and their relationships to contacts in a single query, include the contact’s email and use OBJECT_CONSTRUCT for relationship-specific fields:
SELECT 
  -- Object fields
  p.ID AS "id",
  p.NAME AS "name",
  p.SKU AS "sku",
  p.CATEGORY AS "category",
  p.PRICE AS "price",
  
  -- Contact to relate this object to
  u.EMAIL AS "email",
  
  -- Relationship-specific fields
  OBJECT_CONSTRUCT(
    'role', up.ROLE,
    'quantity', up.QUANTITY,
    'purchased_at', DATE_PART(EPOCH_SECOND, up.PURCHASED_AT)
  ) AS "relationshipFields"
  
FROM user_products up
JOIN products p ON up.PRODUCT_ID = p.ID
JOIN users u ON up.USER_ID = u.ID
WHERE up.UPDATED_AT >= {{last_sync_time}}
In this example:
  • Object fields (id, name, sku, category, price) are stored on the Product object
  • email identifies which contact to create a relationship with
  • relationshipFields contains fields stored on the relationship itself (this contact’s role, quantity, etc.)
If multiple rows share the same object id but have different email values, Conversion creates one object with multiple relationships—one per contact.

Events

SELECT 
  u.EMAIL AS "email",
  'purchase_completed' AS "event_name",
  o.CREATED_AT AS "timestamp",
  CAST(o.ID AS VARCHAR) AS "message_id",
  o.TOTAL_AMOUNT AS "total_amount",
  o.PRODUCT_NAME AS "product_name",
  o.CURRENCY AS "currency"
FROM orders o
JOIN users u ON o.USER_ID = u.ID
WHERE o.CREATED_AT >= {{last_sync_time}}

Snowflake SQL Reference

Converting Timestamps

Conversion expects Unix timestamps for date/time fields. Use DATE_PART(EPOCH_SECOND, column) to convert Snowflake TIMESTAMP values:
SELECT 
  EMAIL AS "email",
  DATE_PART(EPOCH_SECOND, CREATED_AT) AS "created_at",
  DATE_PART(EPOCH_SECOND, LAST_LOGIN) AS "last_login"
FROM users
WHERE UPDATED_AT >= {{last_sync_time}}

Using last_sync_time

The last_sync_time variable is a Unix timestamp. Compare it directly against your timestamp columns:
WHERE UPDATED_AT >= {{last_sync_time}}
Or convert to a Snowflake timestamp:
WHERE UPDATED_AT >= TO_TIMESTAMP({{last_sync_time}})

Building Nested Objects with OBJECT_CONSTRUCT

Use OBJECT_CONSTRUCT to create nested JSON objects like relationshipFields:
OBJECT_CONSTRUCT(
  'role', ROLE,
  'quantity', QUANTITY,
  'started_at', DATE_PART(EPOCH_SECOND, STARTED_AT)
) AS "relationshipFields"

Converting Booleans

Snowflake stores booleans as binary (0/1). Use IFF to convert to string values if needed:
SELECT 
  EMAIL AS "email",
  IFF(IS_ACTIVE, 'true', 'false') AS "is_active",
  IFF(IS_ADMIN, 'true', 'false') AS "is_admin"
FROM users
WHERE UPDATED_AT >= {{last_sync_time}}

Handling NULLs

Use COALESCE or NVL to provide default values:
SELECT 
  EMAIL AS "email",
  COALESCE(FIRST_NAME, '') AS "first_name",
  NVL(PHONE, '') AS "phone"
FROM users

Casting Types

Use CAST or :: to convert between types:
SELECT 
  CAST(USER_ID AS VARCHAR) AS "id",
  SCORE::INTEGER AS "lead_score"
FROM users

Troubleshooting

Column names are still uppercase

If column names remain uppercase despite using AS "lowercase", check the QUOTED_IDENTIFIERS_IGNORE_CASE setting. Set it to FALSE for your user or role:
ALTER USER conversion_sync_user
  SET QUOTED_IDENTIFIERS_IGNORE_CASE = FALSE;
Or for the role:
ALTER ROLE conversion_sync_role
  SET QUOTED_IDENTIFIERS_IGNORE_CASE = FALSE;

“Permission denied” errors

Ensure your role has the required grants:
  • USAGE on the warehouse
  • USAGE on the database and schema
  • SELECT on each table you want to query

”Could not connect” errors

Verify that:
  • Your account identifier is correct (e.g., xy12345.us-east-1)
  • The private key is in PKCS#8 PEM format (starts with -----BEGIN PRIVATE KEY-----)
  • The public key is assigned to your Snowflake user
  • Conversion’s IP addresses are allowed if you use network policies

Sync taking too long

  • Ensure you’re filtering by last_sync_time to reduce rows
  • Select only the columns you need
  • Consider using a larger warehouse size
  • Reduce sync frequency for large datasets

Frequently Asked Questions

The role needs USAGE on the warehouse, database, and schema, plus SELECT on the specific tables you want to sync. Conversion only reads data—it never writes to your Snowflake account.
No. Conversion requires an unencrypted private key in PKCS#8 PEM format. If your key is encrypted, decrypt it first:
openssl pkcs8 -in encrypted_key.pem -out decrypted_key.pem -nocrypt
Use fully-qualified table names in your query:
SELECT u.EMAIL AS "email", o.ORDER_ID AS "order_id"
FROM my_database.users_schema.users u
JOIN my_database.orders_schema.orders o ON u.ID = o.USER_ID
If you sync a custom object with an email that doesn’t exist in Conversion, we create the contact automatically. You can enrich that contact’s profile through a separate contacts sync.