Snowflake
Datafly Signal writes events directly into a Snowflake table so you can analyse first-party behaviour alongside the rest of your warehouse data — no third-party tag required.
Why route Snowflake through Datafly Signal
- The data lands in infrastructure you own. Signal writes to your Snowflake directly from your own deployment. Datafly operates no intermediary and never holds a copy of your event data.
- Personal data is hashed on the way in. Email, phone (shaped to E.164 first), first and last name, full name and postcode are normalised (trim and lowercase) and hashed with SHA-256 inside your own deployment, so the table holds
email_sha256,phone_sha256and the other digests rather than raw personal data. A raw column is an opt-in edit to the installed blueprint. - Client IP and user agent are recorded as event context. Both come from the original visitor request rather than from your server, so geography and device analysis done in the warehouse reflects the actual visitor.
- Presets for Retail, Insurance, Travel, Media & Streaming, Publishing, B2B / SaaS, Automotive and General. Each vertical preset names one field per property in the matching event spec, with canonical names (
transaction_id,value,items), legacy aliases (order_id,revenue,products) read automatically, and numeric properties typed as numbers. Every other event still lands with its properties. - One consent decision governs every destination. Snowflake is gated by the same consent state as the rest of your stack, so a withdrawal applies everywhere at once.
Prerequisites
Before configuring Snowflake in Signal, you need a Snowflake account with a warehouse, database, schema, and a user with appropriate permissions.
Create a Snowflake Account
- Sign up at snowflake.com for a free trial or use an existing account.
- Choose your cloud provider (AWS, Azure, or GCP) and region.
- Note your Account identifier — this includes the account locator and region (e.g.
xy12345.us-east-1). Find it in the Snowflake URL or under Admin > Accounts.
Create a Warehouse
- In the Snowflake console (Snowsight), go to Admin > Warehouses.
- Click + Warehouse.
- Enter a name (e.g.
DATAFLY_WH). - Set the Size (X-Small is sufficient for low to moderate event volume).
- Enable Auto-suspend (e.g. 5 minutes) and Auto-resume to manage costs.
- Click Create Warehouse.
Warehouses are billed per second while running. Auto-suspend and auto-resume ensure you only pay for compute when events are being loaded.
Create a Database and Schema
CREATE DATABASE IF NOT EXISTS ANALYTICS;
CREATE SCHEMA IF NOT EXISTS ANALYTICS.DATAFLY;Create a Table
-- Quoted lowercase names: Signal's INSERT quotes each column, and Snowflake
-- folds unquoted names to upper case, which would not match.
CREATE TABLE ANALYTICS.DATAFLY.EVENTS (
"message_id" STRING,
"event_type" STRING,
"event_name" STRING,
"anonymous_id" STRING,
"user_id" STRING,
"ip_address" STRING,
"user_agent" STRING,
"page_url" STRING,
"page_path" STRING,
"page_referrer" STRING,
"org_id" STRING,
"pipeline_id" STRING,
"event_timestamp" TIMESTAMP_NTZ,
"received_at" TIMESTAMP_NTZ,
"properties_json" VARIANT,
"context_json" VARIANT
);The VARIANT columns store the JSON natively (Signal wraps both in PARSE_JSON). Query nested fields with : notation, e.g. "properties_json":value::FLOAT.
Create a User and Role for Signal
-- Create a role for Signal
CREATE ROLE DATAFLY_SIGNAL_ROLE;
-- Grant warehouse usage
GRANT USAGE ON WAREHOUSE DATAFLY_WH TO ROLE DATAFLY_SIGNAL_ROLE;
-- Grant database and schema usage
GRANT USAGE ON DATABASE ANALYTICS TO ROLE DATAFLY_SIGNAL_ROLE;
GRANT USAGE ON SCHEMA ANALYTICS.DATAFLY TO ROLE DATAFLY_SIGNAL_ROLE;
-- Grant table write permission
GRANT INSERT ON TABLE ANALYTICS.DATAFLY.EVENTS TO ROLE DATAFLY_SIGNAL_ROLE;
-- Create user
CREATE USER DATAFLY_SIGNAL
PASSWORD = 'your_secure_password'
DEFAULT_ROLE = DATAFLY_SIGNAL_ROLE
DEFAULT_WAREHOUSE = DATAFLY_WH;
-- Assign role to user
GRANT ROLE DATAFLY_SIGNAL_ROLE TO USER DATAFLY_SIGNAL;Configuration
| Field | Type | Required | Description |
|---|---|---|---|
account_identifier | string | Yes | Your Snowflake account identifier, including the region suffix (e.g. xy12345.us-east-1). |
warehouse | string | Yes | The Snowflake virtual warehouse to use for loading data. |
database | string | Yes | The Snowflake database containing the target table. |
schema | string | Yes | The Snowflake schema containing the target table. |
table_name | string | Yes | The target table (for example EVENTS). |
username | string | Yes | The Snowflake username for authentication. |
password | secret | Yes | The password for the Snowflake user account. |
role | string | No | Optional Snowflake role to assume when connecting. Defaults to the user’s default role. |
Signal Setup
Quick Setup
- Navigate to Integrations in the sidebar.
- Open the Integration Library tab.
- Find Snowflake or filter by Warehouse.
- Click Install, select a variant if available, and fill in the required fields.
- Click Install Integration to create the integration with a ready-to-use default blueprint.
API Setup
curl -X POST http://localhost:8084/v1/admin/integration-catalog/snowflake/install \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Snowflake",
"variant": "default",
"config": {
"account_identifier": "xy12345.us-east-1",
"warehouse": "DATAFLY_WH",
"database": "ANALYTICS",
"schema": "DATAFLY",
"table_name": "EVENTS",
"username": "DATAFLY_SIGNAL",
"password": "your_secure_password",
"role": "DATAFLY_SIGNAL_ROLE"
},
"delivery_mode": "server_side"
}'Schema
Signal inserts one row per event with an INSERT ... SELECT over these columns (quoted, lower case):
| Column | Snowflake type | Notes |
|---|---|---|
message_id | STRING | Unique per delivered event. |
event_type | STRING | track, page, identify, etc. |
event_name | STRING | Signal event name (snake_case). |
anonymous_id | STRING | First-party visitor identifier. |
user_id | STRING | Logged-in user identifier (nullable). |
ip_address, user_agent | STRING | Client IP and user agent. |
page_url, page_path, page_referrer | STRING | Page context. |
org_id, pipeline_id | STRING | Signal identifiers. |
event_timestamp | TIMESTAMP_NTZ | Client event time. |
received_at | TIMESTAMP_NTZ | Time Signal received the event. |
properties_json | VARIANT | The event as the blueprint shaped it. |
context_json | VARIANT | Page, device and consent context. |
Where mapped fields land
The table layout is fixed: the envelope columns above plus properties_json. The blueprint shapes properties_json: event fields from the preset (for example transaction_id, value, insurance_type, policy_term_months) sit at its top level, and the preset’s envelope block (identity digests, UTM, consent flags) sits under @root:
SELECT "event_timestamp",
"properties_json":transaction_id::STRING AS policy_number,
"properties_json":value::FLOAT AS premium,
"properties_json":"@root".email_sha256::STRING AS email_sha256
FROM ANALYTICS.DATAFLY.EVENTS
WHERE "event_name" = 'purchase';Signal does not write mapped fields to their own typed columns yet. Create views over properties_json for the columns you query often.
Stored event context
Alongside the fields your blueprint maps (where email, phone, names and address are stored as normalised SHA-256 by default), each row carries the event’s collected context exactly as Signal received it. The blueprint does not filter or hash the context: this is deliberate (decision 2026-09-23), so the context is complete for attribution, fraud and debugging queries, and it is listed here so you can decide what to keep.
| Stored | What it contains |
|---|---|
| Visitor request | IP address, user agent, Accept-Language, locale and timezone |
| Page | URL including its query string, path, title and referrer |
| Campaign | UTM source, medium, campaign, term and content |
| Consent | The visitor’s consent state |
| Location | GeoIP country, region and city, and the network (ASN) |
| Device | Screen size; for app events the OS and version, manufacturer, model, advertising ID and IDFV when the app collected them |
| Vendor IDs | Cookie and click identifiers in vendor_ids (for example the _fbp browser ID, gclid, ttclid, the GA client ID) |
| Session and library | Session ID, number and engagement time; the collector’s name and version |
| Partner attributes | Profile attributes from partner enrichment (partner_attrs) |
They are written to the ip_address, user_agent, page_url, page_path and page_referrer columns and, in full, to the context_json column, beside anonymous_id and user_id. Click and collected IDs outside the context are stored only where the blueprint maps them.
The IP address, device and advertising IDs, and cookie and click identifiers are personal data under UK and EU GDPR. To keep any of them out of storage, strip, mask or hash the field for every integration with the organisation data layer, apply URL redaction to page URLs, or remove it in the installed blueprint; and set the storage’s retention to match your privacy notice. See Personal data by integration.
Consent
Snowflake is a first-party destination under your control. The default blueprint forwards all events regardless of consent category, but you can filter on the consent flags in your pipeline transforms or downstream SQL views to honour user choices: "properties_json":"@root".consent_analytics and consent_marketing from the preset, or "context_json":consent.
Testing
- Enable the integration in Signal and trigger a test event on your website.
- Open the Snowflake console and run a query:
SELECT * FROM ANALYTICS.DATAFLY.EVENTS
ORDER BY "event_timestamp" DESC
LIMIT 10;- Verify that event rows are appearing with correct data.
- Test VARIANT querying:
SELECT "message_id", "properties_json":value::FLOAT AS value, "properties_json":currency::STRING AS currency
FROM ANALYTICS.DATAFLY.EVENTS
WHERE "event_name" = 'purchase'
ORDER BY "event_timestamp" DESC
LIMIT 5;- In Signal, check the Live Events view to confirm delivery status shows as successful.
Troubleshooting
| Problem | Solution |
|---|---|
| Events not appearing in Snowflake | Verify the account identifier, warehouse, database, schema, and table are correct. |
Incorrect username or password | The username or password is incorrect. Verify the credentials. Snowflake usernames are case-insensitive but passwords are case-sensitive. |
Insufficient privileges | The role lacks the required permissions. Verify the role has USAGE on the warehouse, database, and schema, plus INSERT on the table. |
| Warehouse suspended | The warehouse auto-suspended. It will auto-resume on the next query but the first request may be slower. Ensure auto-resume is enabled. |
Object does not exist | The database, schema, or table does not exist. Verify the names (Snowflake identifiers are case-sensitive when quoted). |
| Account identifier format | The account identifier must include the region suffix. Check under Admin > Accounts in Snowsight. |
| Connection timeout | Ensure Signal can reach account_identifier.snowflakecomputing.com on port 443. Check network policies and firewall rules. |
| Warehouse credits at high volume | Signal inserts one row per event, and each insert runs on the virtual warehouse. For high-volume pipelines, land events in cloud storage and load them with Snowpipe, or use Snowpipe Streaming. |
Visit Snowflake documentation for full SQL reference, VARIANT query syntax, and cost management guides.