Azure Synapse Analytics

Datafly Signal writes events directly into a Synapse dedicated SQL pool table over the SQL Server wire protocol, so you can analyse first-party behaviour with the rest of your Azure data estate.

Why route Azure Synapse through Datafly Signal

  • The data lands in infrastructure you own. Signal writes to your Azure Synapse directly from your own deployment. Datafly operates no intermediary and never holds a copy of your event data.
  • 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 eight verticals. General (the default: page, identify and the key cross-vertical events), Retail, Insurance, Travel, Media & Streaming, Publishing, B2B / SaaS and Automotive, each writing one named field per documented event property. Email, phone and names are written only as SHA-256 digests unless you deliberately change the installed blueprint.
  • One consent decision governs every destination. Azure Synapse is gated by the same consent state as the rest of your stack, so a withdrawal applies everywhere at once.

Prerequisites

Before configuring Azure Synapse Analytics in Signal, you need an Azure account with a Synapse workspace, a dedicated SQL pool, and a target table.

Create an Azure Account

If you don’t already have one, sign up at azure.microsoft.com.

Create a Synapse Workspace

  1. In the Azure portal, search for Azure Synapse Analytics and click Create.
  2. Select your Subscription and Resource group.
  3. Enter a Workspace name (e.g. datafly-synapse).
  4. Select or create a Data Lake Storage Gen2 account for the workspace.
  5. Set the SQL administrator login and Password (for the built-in serverless SQL pool).
  6. Click Review + Create > Create.

Create a Dedicated SQL Pool

  1. Open your Synapse workspace in the Azure portal.
  2. Go to SQL pools > + New.
  3. Enter a Dedicated SQL pool name (e.g. datafly_pool).
  4. Choose a Performance level (start with DW100c for development, scale up for production).
  5. Click Review + Create > Create.

Dedicated SQL pools are billed while running. Pause the pool when not in use to save costs. Serverless SQL pools are billed per query and do not need to be paused.

Create a Table

Connect to the dedicated SQL pool using Synapse Studio, Azure Data Studio, or SSMS:

CREATE TABLE dbo.signal_events (
    message_id      NVARCHAR(128)  NOT NULL,
    event_type      NVARCHAR(32)   NOT NULL,
    event_name      NVARCHAR(256)  NOT NULL,
    anonymous_id    NVARCHAR(256)  NOT NULL,
    user_id         NVARCHAR(256)  NULL,
    ip_address      NVARCHAR(64)   NULL,
    user_agent      NVARCHAR(1024) NULL,
    page_url        NVARCHAR(4000) NULL,
    page_path       NVARCHAR(2000) NULL,
    page_referrer   NVARCHAR(4000) NULL,
    org_id          NVARCHAR(64)   NULL,
    pipeline_id     NVARCHAR(64)   NULL,
    event_timestamp DATETIME2      NULL,
    received_at     DATETIME2      NULL,
    properties_json NVARCHAR(MAX)  NULL,
    context_json    NVARCHAR(MAX)  NULL
)
WITH (
    DISTRIBUTION = ROUND_ROBIN,
    HEAP
);

Signal inserts one row per event, so a round-robin heap is the right landing table: it takes single-row inserts without columnstore delta-store overhead. Copy into a hash-distributed columnstore table on a schedule (CTAS or INSERT ... SELECT) for analytics. The column names must match exactly; the table can have extra nullable columns.

Get the Server Endpoint

In the Synapse workspace overview, copy the Dedicated SQL endpoint (e.g. datafly-synapse.sql.azuresynapse.net). Synapse listens on TCP port 1433 and requires Encrypt=True.

Configure Firewall Rules

  1. In the Synapse workspace, go to Security > Firewalls.
  2. Add the IP addresses of your Signal deployment.
  3. Click Save.

Configuration

FieldTypeRequiredDescription
serverstringYesThe dedicated SQL endpoint, for example datafly-synapse.sql.azuresynapse.net.
portstringNoDefault 1433.
databasestringYesThe dedicated SQL pool (database) name.
schema_namestringNoDefault dbo.
table_namestringYesThe table created above.
usernamestringYesSQL authentication user with INSERT and SELECT on the table.
passwordsecretYesThe user’s password.
encryptstringNotrue (default) requires TLS, which Synapse requires.

Signal Setup

Quick Setup

  1. Navigate to Integrations in the sidebar.
  2. Open the Integration Library tab.
  3. Find Azure Synapse Analytics or filter by Warehouse.
  4. Click Install, select a variant if available, and fill in the required fields.
  5. 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/azure_synapse/install \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Azure Synapse Analytics",
    "variant": "default",
    "config": {
      "server": "datafly-synapse.sql.azuresynapse.net",
      "port": "1433",
      "database": "datafly_pool",
      "schema_name": "dbo",
      "table_name": "events",
      "username": "sqladmin",
      "password": "YOUR_PASSWORD",
      "encrypt": "true"
    },
    "delivery_mode": "server_side"
  }'

What Signal writes

Each event is one row, written with a single parameterised statement that skips a message_id already in the table, so a retried delivery does not create a duplicate row:

IF NOT EXISTS (SELECT 1 FROM [dbo].[signal_events] WHERE message_id = @p1)
INSERT INTO [dbo].[signal_events] ([message_id], [event_type], [event_name], [anonymous_id], [user_id], [ip_address], [user_agent], [page_url], [page_path], [page_referrer], [org_id], [pipeline_id], [event_timestamp], [received_at], [properties_json], [context_json])
VALUES (@p1, ..., @p16)
ColumnContents
message_idSignal’s message ID (deduplication key).
event_type, event_nameEvent type (track, page, identify, …) and the Signal event name.
anonymous_id, user_idVisitor identifiers (user_id NULL when not set).
ip_address, user_agent, page_url, page_path, page_referrerFrom the event context.
org_id, pipeline_idWhere the event came from.
event_timestamp, received_atEvent time and Signal receipt time (UTC).
properties_jsonThe mapped properties object as JSON text (below).
context_jsonThe whole event context as JSON text, written in full (#1414).

Only what the blueprint maps is written. The properties_json column holds the fields your installed blueprint maps and nothing else (the row contract): the global block under @root (copies of the envelope fields, UTM parameters, the analytics and marketing consent flags, and email_sha256, phone_sha256, first_name_sha256, last_name_sha256, name_sha256), and the event section’s fields at the top level (transaction_id, value, currency, items, …). Identify traits and client properties the blueprint does not map are never written. An event with no section in the blueprint still arrives, with the @root block only: add a section for any other event whose properties you need.

Query the JSON columns with JSON_VALUE, for example JSON_VALUE(properties_json, '$.transaction_id') or JSON_VALUE(properties_json, '$."@root".email_sha256').

⚠️

Only a dedicated SQL pool accepts INSERT; a serverless SQL pool is read-only over the data lake (write to Azure Data Lake Storage instead). Microsoft recommends COPY for bulk loading: one insert per event, each checking the table for its message_id, suits modest volumes. At high volume, land events in Azure Data Lake Storage and load them with COPY.

The rows above were produced by Signal’s Synapse writer against SQL Server 2022 (the same T-SQL wire protocol; the WITH (DISTRIBUTION ...) clause is Synapse-only and was left out there) on 2026-09-23 (batch S1 review): three events and a retried purchase gave three rows.

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.

StoredWhat it contains
Visitor requestIP address, user agent, Accept-Language, locale and timezone
PageURL including its query string, path, title and referrer
CampaignUTM source, medium, campaign, term and content
ConsentThe visitor’s consent state
LocationGeoIP country, region and city, and the network (ASN)
DeviceScreen size; for app events the OS and version, manufacturer, model, advertising ID and IDFV when the app collected them
Vendor IDsCookie and click identifiers in vendor_ids (for example the _fbp browser ID, gclid, ttclid, the GA client ID)
Session and librarySession ID, number and engagement time; the collector’s name and version
Partner attributesProfile 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.

The integration’s consent category in Signal decides which events reach Azure Synapse; events that fail it are never written. Every row also carries the visitor’s consent state (context.consent / context_json, and consent_analytics / consent_marketing in @root) so downstream queries can apply their own rules.

Testing

  1. Enable the integration in Signal and trigger a test event on your website.
  2. Open Synapse Studio and navigate to the dedicated SQL pool.
  3. Run a query to check for events:
SELECT TOP 10 * FROM dbo.events ORDER BY timestamp DESC;
  1. Verify that event rows are appearing with correct data.
  2. In Signal, check the Live Events view to confirm delivery status shows as successful.

Troubleshooting

ProblemSolution
Serverless SQL poolA serverless pool cannot store rows. Point Signal at a dedicated SQL pool.
Invalid column nameThe table’s columns must be the 16 in What Signal writes, with those exact names.
Events not appearing in the tableVerify the connection string, database, schema, and table name are correct. Ensure the dedicated SQL pool is running (not paused).
Connection refused / timeoutCheck the Synapse firewall rules. Add Signal’s IP addresses.
Login failedVerify the username and password in the connection string.
INSERT permission deniedThe user needs INSERT and SELECT (the duplicate check reads the table): GRANT INSERT, SELECT ON dbo.signal_events TO your_user;.
SQL pool is pausedDedicated SQL pools must be running to accept connections. Resume the pool in the Azure portal.
Invalid object nameThe table does not exist or the schema is incorrect. Verify with SELECT * FROM INFORMATION_SCHEMA.TABLES;.
Performance issuesCheck the SQL pool performance level (DWU). Scale up if insert throughput is insufficient.

Visit Azure Synapse Analytics documentation for full T-SQL reference, distribution strategies, and performance tuning guides.

See also