> ## Documentation Index
> Fetch the complete documentation index at: https://docs.permutive.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Snowflake

> Stream first-party event data to Snowflake via routing and import audience data for cohort building and activation

export const NoBadge = () => {
  return <span style={{
    display: 'inline-block',
    padding: '0.125rem 0.5rem',
    borderRadius: '0.25rem',
    fontSize: '0.625rem',
    background: '#F7D0E2',
    color: '#1A1A1A',
    fontWeight: '500'
  }}>
      No
    </span>;
};

export const YesBadge = () => {
  return <span style={{
    display: 'inline-block',
    padding: '0.125rem 0.5rem',
    borderRadius: '0.25rem',
    fontSize: '0.625rem',
    background: '#C7E8F9',
    color: '#1A1A1A',
    fontWeight: '500'
  }}>
      Yes
    </span>;
};

export const BadgeRowCenter = ({label, children}) => {
  return <div style={{
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: '0.5rem'
  }}>
      <span style={{
    fontSize: '0.625rem',
    color: '#6b7280',
    textTransform: 'uppercase',
    fontWeight: '500',
    letterSpacing: '0.05em'
  }}>
        {label}
      </span>
      {children}
    </div>;
};

export const BadgeRow = ({label, children}) => {
  return <div style={{
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'flex-start',
    marginBottom: '0.5rem'
  }}>
      <span style={{
    fontSize: '0.625rem',
    color: '#6b7280',
    textTransform: 'uppercase',
    fontWeight: '500',
    letterSpacing: '0.05em'
  }}>
        {label}
      </span>
      {children}
    </div>;
};

export const BadgeContainer = ({children}) => {
  return <div style={{
    display: 'flex',
    gap: '0.25rem',
    flexWrap: 'wrap',
    justifyContent: 'flex-end',
    minWidth: '0',
    flex: '1'
  }}>
      {children}
    </div>;
};

export const ProductRequiredBadge = ({product}) => {
  const getBadgeStyle = product => {
    switch (product) {
      case 'Core Platform':
        return {
          background: '#CB88FC',
          color: '#1A1A1A'
        };
        --purple;
      case 'Routing':
        return {
          background: '#CB88FC',
          color: '#1A1A1A'
        };
        --purple;
      case 'Contextual':
        return {
          background: '#CB88FC',
          color: '#1A1A1A'
        };
        --purple;
      default:
        return {
          background: '#A7B3D9',
          color: '#1A1A1A'
        };
        --haze;
    }
  };
  const style = getBadgeStyle(product);
  return <span style={{
    display: 'inline-block',
    padding: '0.125rem 0.375rem',
    borderRadius: '0.25rem',
    fontSize: '0.625rem',
    background: style.background,
    color: style.color,
    fontWeight: '500'
  }}>
      {product}
    </span>;
};

export const SdkRequiredBadge = ({required}) => {
  const getBadgeStyle = required => {
    switch (required) {
      case 'Yes':
        return {
          background: '#C7E8F9',
          color: '#1A1A1A'
        };
        --blue;
      case 'No':
        return {
          background: '#F7D0E2',
          color: '#1A1A1A'
        };
        --pink;
      default:
        return {
          background: '#A7B3D9',
          color: '#1A1A1A'
        };
        --haze;
    }
  };
  const style = getBadgeStyle(required);
  return <span style={{
    display: 'inline-block',
    padding: '0.125rem 0.375rem',
    borderRadius: '0.25rem',
    fontSize: '0.625rem',
    background: style.background,
    color: style.color,
    fontWeight: '500'
  }}>
      {required}
    </span>;
};

export const CapabilityBadge = ({capability}) => {
  const getBadgeStyle = capability => {
    switch (capability) {
      case 'Event Collection':
        return {
          background: '#EFDFC8',
          color: '#1A1A1A'
        };
        --clay;
      case 'Cohort Activation':
        return {
          background: '#EFDFC8',
          color: '#1A1A1A'
        };
        --clay;
      case 'Campaign Optimization':
        return {
          background: '#EFDFC8',
          color: '#1A1A1A'
        };
        --clay;
      case 'Identity Signal':
        return {
          background: '#EFDFC8',
          color: '#1A1A1A'
        };
        --clay;
      case 'Contextual Signal':
        return {
          background: '#EFDFC8',
          color: '#1A1A1A'
        };
        --clay;
      case 'Connectivity':
        return {
          background: '#EFDFC8',
          color: '#1A1A1A'
        };
        --clay;
      case 'Routing':
        return {
          background: '#EFDFC8',
          color: '#1A1A1A'
        };
        --clay;
      case 'Data Collaboration':
        return {
          background: '#EFDFC8',
          color: '#1A1A1A'
        };
        --clay;
      default:
        return {
          background: '#A7B3D9',
          color: '#1A1A1A'
        };
        --haze;
    }
  };
  const style = getBadgeStyle(capability);
  return <span style={{
    display: 'inline-block',
    padding: '0.125rem 0.375rem',
    borderRadius: '0.25rem',
    fontSize: '0.625rem',
    background: style.background,
    color: style.color,
    fontWeight: '500',
    whiteSpace: 'nowrap'
  }}>
      {capability}
    </span>;
};

export const EnvironmentBadge = ({environment}) => {
  const getBadgeStyle = environment => {
    switch (environment) {
      case 'Web':
        return {
          background: '#F9C1A8',
          color: '#1A1A1A'
        };
        --peach;
      case 'iOS':
        return {
          background: '#F9C1A8',
          color: '#1A1A1A'
        };
        --peach;
      case 'Android':
        return {
          background: '#F9C1A8',
          color: '#1A1A1A'
        };
        --peach;
      case 'CTV':
        return {
          background: '#F9C1A8',
          color: '#1A1A1A'
        };
        --peach;
      case 'API Direct':
        return {
          background: '#F9C1A8',
          color: '#1A1A1A'
        };
        --peach;
      default:
        return {
          background: '#A7B3D9',
          color: '#1A1A1A'
        };
        --haze;
    }
  };
  const style = getBadgeStyle(environment);
  return <span style={{
    display: 'inline-block',
    padding: '0.125rem 0.375rem',
    borderRadius: '0.25rem',
    fontSize: '0.625rem',
    background: style.background,
    color: style.color,
    fontWeight: '500',
    whiteSpace: 'nowrap'
  }}>
      {environment}
    </span>;
};

export const DirectionBadge = ({direction}) => {
  const getBadgeStyle = direction => {
    switch (direction) {
      case 'Bidirectional':
        return {
          background: '#FA8784',
          color: '#1A1A1A'
        };
        --tomato;
      case 'Destination':
        return {
          background: '#FA8784',
          color: '#1A1A1A'
        };
        --tomato;
      case 'Source':
        return {
          background: '#FA8784',
          color: '#1A1A1A'
        };
        --tomato;
      default:
        return {
          background: '#A7B3D9',
          color: '#1A1A1A'
        };
        --haze;
    }
  };
  const style = getBadgeStyle(direction);
  return <span style={{
    display: 'inline-block',
    padding: '0.125rem 0.375rem',
    borderRadius: '0.25rem',
    fontSize: '0.625rem',
    background: style.background,
    color: style.color,
    fontWeight: '500'
  }}>
      {direction}
    </span>;
};

<Card title="">
  <div style={{ display: 'flex', alignItems: 'center', marginBottom: '1rem' }}>
    <div style={{ width: '32px', height: '32px', marginRight: '0.75rem', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
      <img src="https://mintcdn.com/permutive/pNhz39ducTVcQczh/images/integrations/logos/snowflake.svg?fit=max&auto=format&n=pNhz39ducTVcQczh&q=85&s=8eb1aaa8c9ae237cf135152560643ed2" alt="Snowflake" style={{ maxWidth: '32px', maxHeight: '32px', display: 'block' }} width="800" height="762" data-path="images/integrations/logos/snowflake.svg" />
    </div>

    <h3 style={{ margin: 0, fontSize: '1.125rem', fontWeight: '600' }}>Snowflake</h3>
  </div>

  <div style={{ marginBottom: '1rem' }}>
    <BadgeRowCenter label="Direction">
      <DirectionBadge direction="Bidirectional" />
    </BadgeRowCenter>

    <BadgeRowCenter label="Environment">
      <BadgeContainer>
        <EnvironmentBadge environment="Web" />

        <EnvironmentBadge environment="iOS" />

        <EnvironmentBadge environment="Android" />

        <EnvironmentBadge environment="CTV" />

        <EnvironmentBadge environment="API Direct" />
      </BadgeContainer>
    </BadgeRowCenter>

    <BadgeRowCenter label="Capability">
      <BadgeContainer>
        <CapabilityBadge capability="Connectivity" />

        <CapabilityBadge capability="Routing" />
      </BadgeContainer>
    </BadgeRowCenter>

    <BadgeRowCenter label="SDK Required">
      <SdkRequiredBadge required="No" />
    </BadgeRowCenter>

    <BadgeRowCenter label="Product(s) Required">
      <BadgeContainer>
        <ProductRequiredBadge product="Core Platform" />

        <ProductRequiredBadge product="Routing" />
      </BadgeContainer>
    </BadgeRowCenter>
  </div>

  <p style={{ margin: 0, fontSize: '0.875rem', color: '#6b7280', lineHeight: '1.5' }}>
    Snowflake provides a cloud data platform where publishers can unify and analyze audience and advertising data securely.
  </p>
</Card>

<CardGroup cols={2}>
  <Card title="Setup" href="#setup" icon="gear" />

  <Card title="Troubleshooting" href="#troubleshooting" icon="wrench" />
</CardGroup>

## Overview

The Snowflake integration enables publishers to leverage Permutive's bi-directional data capabilities with their Snowflake data warehouse. This integration operates in two modes:

**Routing (Destination)**: Stream first-party event data from Permutive to Snowflake. Read more in [Routing](/products/connectivity/routing) documentation.

<Note>
  Routing capability requires the Routing package in addition to Core Platform. Contact your Customer Success Manager to enable Routing.
</Note>

**Connectivity (Source)**: Import audience data from your Snowflake warehouse into Permutive for cohort building and activation across your publisher inventory.

Use cases include:

* Unifying first-party audience data with advertising and CRM data in Snowflake
* Building a single view of your audience by combining Permutive data with other data sources
* Creating segments and insights in Snowflake for activation in Permutive
* Running custom analytics and reporting on raw Permutive event data

## Environment Compatibility

| Environment    | Supported    | Notes |
| -------------- | ------------ | ----- |
| **Web**        | <YesBadge /> | --    |
| **iOS**        | <YesBadge /> | --    |
| **Android**    | <YesBadge /> | --    |
| **CTV**        | <YesBadge /> | --    |
| **API Direct** | <YesBadge /> | --    |

## Prerequisites

For **Routing (exporting data to Snowflake)**:

* Active Snowflake account with appropriate permissions
* Ability to create database, schema, tables, stages, and pipes in Snowflake
* Ability to create users with key-pair authentication (password authentication is not supported)
* Access to a Snowflake warehouse for data loading operations
* Snowflake account URL in the format: `https://<organization-id>-<account-id>.snowflakecomputing.com`

## Setup

<Tabs>
  <Tab title="Routing Streaming Setup">
    Setting up Snowflake routing involves creating a dedicated database, schema, user, and role in your Snowflake instance, then configuring key-pair authentication so Permutive can stream event data via Snowpipe. Once configured, Permutive will complete the storage integration and begin streaming data automatically.

    ### Prerequisites

    * Active Snowflake account with administrative permissions
    * Ability to create databases, schemas, users, roles, and storage integrations
    * Access to a Snowflake warehouse
    * Snowflake account URL in the format: `https://<organization-id>-<account-id>.snowflakecomputing.com`

    <Warning>
      Snowflake Routing requires key-pair authentication. Password authentication is not supported.
    </Warning>

    For the complete step-by-step setup guide with SQL scripts and permissions configuration, see [Setting Up Snowflake Routing](/guides/connectivity/routing/setting-up-snowflake-routing).

    ### What Happens After Setup

    Once routing is active:

    1. **Tables are created automatically** for events, aliases, and segment metadata
    2. **Events stream via Snowpipe** in approximately 5-minute or 500MB batches
    3. **All event types appear** in a single `EVENTS` table with an `EVENTNAME` column to distinguish types
    4. **Schemas update automatically** when you add new event types or properties
  </Tab>

  <Tab title="Connectivity Setup">
    ### Prerequisites

    * A Snowflake account with `SECURITYADMIN` and `ACCOUNTADMIN` roles
    * Access to run SQL scripts in your Snowflake instance
    * Knowledge of the database, schema, and warehouse you want to connect

    ### Step 1: Set Up Your Snowflake Instance

    Run a setup script to create a dedicated user and role with read-only permissions. Choose between password authentication or key pair authentication.

    <Tabs>
      <Tab title="Password Authentication">
        Replace the placeholders and run the following script:

        ```sql theme={"dark"}
        begin;
           set role_name = 'PERMUTIVE_ROLE';
           set user_name = 'PERMUTIVE_USER';
           set user_password = '<PASSWORD>';
           set warehouse_name = '<WAREHOUSE_NAME>';
           set database_name = '<DATABASE_NAME>';
           set schema_name = '<SCHEMA_NAME>';

           use role securityadmin;
           create role if not exists identifier($role_name);
           create user if not exists identifier($user_name)
             password = $user_password
             default_role = $role_name
             default_warehouse = $warehouse_name;
           grant role identifier($role_name) to user identifier($user_name);

           use role ACCOUNTADMIN;
           grant usage on warehouse identifier($warehouse_name) to role identifier($role_name);
           grant usage on database identifier($database_name) to role identifier($role_name);
           use database identifier($database_name);
           grant usage on schema identifier($schema_name) to role identifier($role_name);
           grant select on all TABLES in schema identifier($schema_name) to role identifier($role_name);
           grant select on future TABLES in schema identifier($schema_name) to role identifier($role_name);
        commit;
        ```
      </Tab>

      <Tab title="Key Pair Authentication">
        First generate a key pair, then run the setup script with the public key.

        See [Connecting to Snowflake](/guides/connectivity/sources/connecting-to-snowflake) for complete key pair setup instructions.
      </Tab>
    </Tabs>

    ### Step 2: Configure Network Policy (Optional)

    If you use Network Policies, add Permutive's IP addresses to your allowlist. See the [complete guide](/guides/connectivity/sources/connecting-to-snowflake#step-2-configure-network-policy-optional) for the full list of IPs.

    ### Step 3: Create the Connection in Permutive

    <Steps>
      <Step title="Select Snowflake from the Catalog">
        In the Permutive dashboard, go to **Connectivity > Catalog** and select **Snowflake**.
      </Step>

      <Step title="Enter Connection Details">
        | Field         | Description                                            |
        | :------------ | :----------------------------------------------------- |
        | **Database**  | The database name (use UPPERCASE)                      |
        | **Host**      | Your Snowflake account URL (without `https://` prefix) |
        | **Port**      | Leave as default `443`                                 |
        | **User**      | The user created by the script (`PERMUTIVE_USER`)      |
        | **Role**      | The role created by the script (`PERMUTIVE_ROLE`)      |
        | **Password**  | The password from your script                          |
        | **Warehouse** | The Snowflake compute warehouse to use                 |

        <Warning>
          Snowflake uses UPPERCASE for all database, schema, user, and role names.
        </Warning>
      </Step>

      <Step title="Save the Connection">
        Click **Save** to create the connection. It will appear on your **Connections** page with a "Processing" status while Permutive validates the credentials.
      </Step>
    </Steps>

    ### Step 4: Create an Import

    Once your connection is active, go to **Connectivity > Imports** and click **Create Import**, then select your Snowflake connection.

    For the complete setup guide with detailed instructions, see [Connecting to Snowflake](/guides/connectivity/sources/connecting-to-snowflake).
  </Tab>
</Tabs>

## Data Types

When Routing is enabled, Permutive creates the following tables in your Snowflake instance to store event data and metadata:

<AccordionGroup>
  <Accordion title="EVENTS Table">
    The main events table contains all user interaction data collected by Permutive.

    <ResponseField name="TIME" type="TIMESTAMP">
      The timestamp when the event occurred.
    </ResponseField>

    <ResponseField name="ORGANIZATIONID" type="VARCHAR">
      Your Permutive organization identifier.
    </ResponseField>

    <ResponseField name="PROJECTID" type="VARCHAR">
      The workspace (project) ID where the event was collected.
    </ResponseField>

    <ResponseField name="VIEWID" type="VARCHAR">
      Unique identifier for the page view session.
    </ResponseField>

    <ResponseField name="SESSIONID" type="VARCHAR">
      Unique identifier for the user session.
    </ResponseField>

    <ResponseField name="USERID" type="VARCHAR">
      The Permutive user identifier.
    </ResponseField>

    <ResponseField name="EVENTID" type="VARCHAR">
      Unique identifier for this specific event.
    </ResponseField>

    <ResponseField name="EVENTNAME" type="VARCHAR">
      The name/type of the event (e.g., PageView, Pageview, SlotViewable).
    </ResponseField>

    <ResponseField name="SEGMENTS" type="ARRAY">
      Array of segment IDs (integers) that the user belongs to at the time of the event.
    </ResponseField>

    <ResponseField name="PROPERTIES" type="OBJECT">
      JSON object containing event-specific properties and metadata. Structure varies by event type.
    </ResponseField>
  </Accordion>

  <Accordion title="SYNC_ALIASES Table">
    Contains user identity synchronization data for cross-device and cross-context tracking.

    <ResponseField name="TIME" type="TIMESTAMP">
      The timestamp when the alias sync occurred.
    </ResponseField>

    <ResponseField name="EVENT_TYPE" type="VARCHAR">
      Type of sync event (e.g., "alias\_sync").
    </ResponseField>

    <ResponseField name="PERMUTIVE_ID" type="VARCHAR">
      The Permutive user identifier.
    </ResponseField>

    <ResponseField name="WORKSPACE_ID" type="VARCHAR">
      The workspace (project) ID.
    </ResponseField>

    <ResponseField name="ID" type="VARCHAR">
      The external identifier being synced (e.g., hashed email, device ID).
    </ResponseField>

    <ResponseField name="TAG" type="VARCHAR">
      The type/tag of the external identifier (e.g., "email\_sha256", "device\_id").
    </ResponseField>
  </Accordion>

  <Accordion title="SEGMENTS Table">
    Metadata table containing information about segments configured in your Permutive workspace.

    <ResponseField name="INSERTED_AT" type="TIMESTAMP">
      When the segment metadata was inserted/updated in Snowflake.
    </ResponseField>

    <ResponseField name="WORKSPACE_ID" type="VARCHAR">
      The workspace (project) ID.
    </ResponseField>

    <ResponseField name="NAME" type="VARCHAR">
      Human-readable name of the segment.
    </ResponseField>

    <ResponseField name="TAGS" type="ARRAY">
      Array of tags associated with the segment.
    </ResponseField>

    <ResponseField name="METADATA" type="OBJECT">
      JSON object containing additional segment metadata (e.g., description, creation date).
    </ResponseField>

    <ResponseField name="NUMBER" type="VARCHAR">
      The segment ID number as a string.
    </ResponseField>
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Data not appearing in Snowflake after setup">
    **Symptoms**: No data visible in the EVENTS table after completing Routing setup.

    **Solutions**:

    1. Wait at least 5-10 minutes after setup completion for initial data flow
    2. Verify Snowpipe is running:
       ```sql theme={"dark"}
       SHOW PIPES IN SCHEMA PERMUTIVE.DATA;
       ```
    3. Check pipe status - it should show as "RUNNING"
    4. Verify the storage integration was created successfully:
       ```sql theme={"dark"}
       SHOW INTEGRATIONS;
       ```
    5. Contact [Support](mailto:support@permutive.com) if no data appears after 15 minutes
  </Accordion>

  <Accordion title="Key-pair authentication errors">
    **Symptoms**: Authentication failures when connecting to Snowflake, errors like "Invalid key" or "Authentication failed".

    **Solutions**:

    1. Ensure the private key is in PKCS8 PEM format:
       ```bash theme={"dark"}
       openssl pkcs8 -topk8 -inform PEM -in old_key.pem -out new_key.p8 -nocrypt
       ```
    2. When setting `RSA_PUBLIC_KEY` on the Snowflake user, ensure you:
       * Remove the `-----BEGIN PUBLIC KEY-----` header
       * Remove the `-----END PUBLIC KEY-----` footer
       * Concatenate all remaining lines into a single string (no line breaks)
    3. Verify the public key is correctly attached to the user:
       ```sql theme={"dark"}
       DESC USER permutive_routing;
       ```
       Check the `RSA_PUBLIC_KEY_FP` field is populated
    4. If using an existing key pair, regenerate a fresh key pair following the setup instructions
  </Accordion>

  <Accordion title="Permission denied errors">
    **Symptoms**: Errors indicating insufficient permissions when Permutive attempts to create tables, stages, or pipes.

    **Solutions**:

    1. Verify the role has all required permissions:
       ```sql theme={"dark"}
       SHOW GRANTS TO ROLE permutive_routing_role;
       ```
    2. Ensure the following grants are present:
       * `USAGE` on database
       * `USAGE, CREATE TABLE, CREATE STAGE, CREATE PIPE` on schema
       * `CREATE INTEGRATION` on account
       * `OPERATE` on future pipes
       * `USAGE` on warehouse
    3. If any grants are missing, re-run the permission grant commands from the setup guide
    4. Ensure the user's default role is set correctly:
       ```sql theme={"dark"}
       ALTER USER permutive_routing SET DEFAULT_ROLE = permutive_routing_role;
       ```
  </Accordion>
</AccordionGroup>

## Changelog

<Info>
  No changes listed yet. For detailed changelog information, visit our [Changelog](https://changelog.permutive.com/).
</Info>
