Technical reference

Data Structure for Visualization

Connect Tableau, Power BI, Looker, or another reporting tool to the versioned BI views provided by Optin Model Analytics without exposing private event rows.

Use the supported views as the reporting boundary

Routine BI credentials should receive SELECT access only to bi_anonymous_events_v1, bi_anonymous_goals_v1, bi_anonymous_daily_v1, bi_anonymous_daily_rollup_v1, bi_anonymous_coverage_v1, and, when needed, bi_anonymous_geo_events_v1, plus the declared bi_dim_* and bi_glossary_* metadata views needed for labels. Do not grant routine reporting users access to events, analytics_glossary, or other application tables.

Showing all 8 sections

Supported BI contract

bi_anonymous_events_v1

Anonymous only Hourly grain

Each row is one released hourly cell, grouped by every dimension listed below. The current UTC hour and cells below anonymous_min_cell_count are absent. The default threshold is 5 events and the allowed range is 2–1000.

Columns exposed by bi_anonymous_events_v1
Column Logical type Meaning
website_token String Site key used to partition events. Website names and domains are stored outside the database.
event_hour UTC datetime Start of a completed UTC hour; anonymous event times are bucketed to this boundary.
event_name String view for page views, or a fixed safe name for a measured interaction.
page_path Text Sanitized path with query strings and fragments removed and identifier-like segments redacted.
referrer_channel Category direct, internal, search, social, email, referral, or unknown.
device_class Category mobile, tablet, desktop, bot, or unknown.
viewport_bucket Category small, medium, large, or unknown.
event_count Integer Number of event rows in the released cell. This is not a count of distinct people.
The row grain is the combination of the first seven columns. Sum event_count with SUM(...) when removing dimensions from a report. This totals already-released cells; it does not re-run suppression at the broader grain or recover hidden cells.

Conversion BI contract

bi_anonymous_goals_v1

Anonymous only Daily grain

Each row is one released daily goal cell. The view includes only anonymous events with an allowlisted goal retained in goal_event. The current UTC day and cells below anonymous_min_cell_count are absent. The default threshold is 5 goal occurrences and the allowed range is 2–1000.

Columns exposed by bi_anonymous_goals_v1
Column Logical type Meaning
website_token String Site key used to partition goal occurrences.
event_day UTC date A completed UTC calendar day.
goal_event String Stable allowlisted code from config/goals.yaml; labels are presentation metadata and are not stored in this view.
event_count Integer Number of goal occurrences in the released cell, not distinct people or unique converters.
This view deliberately excludes event name, path, referrer, device, viewport, geography, and identifiers. Do not join it to the hourly or geography views: they have different grains and independent suppression. A configured goal can still be sensitive in context, and a generic rejection warning is an allowlist result—not sensitive-data detection.

Approved BI contract

bi_anonymous_daily_v1

Anonymous only Daily grain

Each row is one released daily cell by page group, referrer channel and device class. Grouping pages and dropping the hour and viewport releases far more of a low-traffic site's events than the hourly view. A cell is withheld below anonymous_min_cell_count, and also when the part of it the hourly view withholds would be smaller than that minimum, so comparing the two views never reveals a small count. Page groups are configured under Reporting → Page groups or as bi_page_groups in YAML.

Columns exposed by bi_anonymous_daily_v1
Column Logical type Meaning
website_token String Site key used to partition events.
event_day UTC date A completed UTC calendar day.
event_name String view for page views, or a fixed safe interaction name.
page_group Text The label of the first matching page group rule, or the sanitized path when none matches.
referrer_channel Category Coarse source channel.
device_class Category Coarse device category.
event_count Integer Number of events in the released daily cell, not distinct people.

Approved BI contract

bi_anonymous_daily_rollup_v1

Anonymous only Daily grain

The daily cells, with the ones too small to release rolled up into coarser rows instead of dropped: devices pooled first, then channels, then pages, then everything for the event and day. A pooled value is (other), meaning the part not shown in more detailed rows. Every row reaches anonymous_min_cell_count and follows the same rule as the daily view against the hourly view, and each event is in at most one row, so daily totals add up. The full-detail rows are exactly the daily view's rows.

Columns exposed by bi_anonymous_daily_rollup_v1
Column Logical type Meaning
website_token String Site key used to partition events.
event_day UTC date A completed UTC calendar day.
event_name String Kept in every row.
page_group Text Page group label, sanitized path, or (other).
referrer_channel Category Coarse source channel, or (other).
device_class Category Coarse device category, or (other).
event_count Integer Number of events in the row, not distinct people.
detail_level Category page_channel_device, page_channel, page, channel or day: which dimensions are known.

Approved BI contract

bi_anonymous_coverage_v1

Anonymous only Daily grain

How much of each completed day's anonymous traffic each event view releases, per website, as a band rather than a number, so a report can say how complete it is without revealing the day's total. Days with fewer events than anonymous_min_cell_count have no rows.

Columns exposed by bi_anonymous_coverage_v1
Column Logical type Meaning
website_token String Site key used to partition events.
event_day UTC date A completed UTC calendar day.
view_name String bi_anonymous_events_v1, bi_anonymous_daily_v1 or bi_anonymous_daily_rollup_v1.
coverage_band Category 90-100, 50-90, 10-50 or 0-10: percent of the day's events the view releases.

Optional BI contract

bi_anonymous_geo_events_v1

Anonymous only Daily grain

Each row is one released daily geography cell. The current UTC day and cells below anonymous_geo_min_cell_count are not shown individually; their counts may be released only in a qualifying other pool. The default threshold is 25 events and the allowed range is 10–1000.

Columns exposed by bi_anonymous_geo_events_v1
Column Logical type Meaning
website_token String Site key used to partition events.
event_day UTC date A completed UTC calendar day.
event_name String view for page views, or a fixed safe interaction name.
geo_area Category continent:XX, country:XX, or a suppressed pool such as continent:other or country:other.
event_count Integer Number of event rows in the released geography cell, not distinct people.
This view deliberately excludes path, referrer, device, viewport, and identifiers. Do not join it to the hourly view: the views have different grains and independent suppression. Low-volume areas may be combined into an other pool, including one otherwise-visible area when secondary suppression is needed. Events without a retained geography value are excluded, so geography totals will not match the hourly view.

Metadata reference

BI labels and definitions

8 dimension views

Join declared metadata to reporting codes to display labels such as Tablet or Germany. These views contain configuration and built-in definitions, never observed traffic or counts. Administrators publish them with app:analytics:glossary:sync after migrations or metadata changes, or save through Reporting → BI glossary.

Approved glossary view contracts
ViewJoin key and purpose
bi_dim_event_name_v1event_name; declared event labels.
bi_dim_goal_event_v1goal_event; configured goal labels.
bi_dim_referrer_channel_v1referrer_channel; all built-in channel codes.
bi_dim_device_class_v1device_class; all five device codes.
bi_dim_viewport_bucket_v1viewport_bucket; all viewport codes.
bi_dim_geo_area_v1geo_area; all continents and countries, plus geo_level.
bi_dim_website_token_v1website_token; registered website names and domains, plus declared labels.
bi_glossary_values_v1Filter to one dimension and locale, then join code to the fact's code column.
bi_glossary_columns_v1Filter by object_name and locale for column labels and definitions.

The six dimension views have one row per code in the deployment's default locale, with prefixed fields such as device_class_label and device_class_sort. Relate each one many-to-one to the matching fact column and display the label. For translated labels, filter bi_glossary_values_v1 to one dimension and one published locale before joining; joining all locales multiplies fact rows.

Use a left join and keep the original code when a label is missing: undeclared event names, deleted goals, and the synthetic country:other/continent:other pools have no dimension row.

SELECT f.event_day, f.goal_event,
       COALESCE(d.goal_event_label, f.goal_event) AS goal_label,
       f.event_count
FROM bi_anonymous_goals_v1 f
LEFT JOIN bi_dim_goal_event_v1 d ON d.goal_event = f.goal_event;
Labels, descriptions, and declared codes are published to routine BI readers; keep them free of personal or identifying text. Declaring values for consent-gated properties publishes those codes even when their event values remain private. Glossary joins do not restore withheld cells: sums by group still undercount. Column descriptions for private custom views do not grant access to those facts. Keep the hourly, goal, and geography facts separate.

Extended schema

Custom reporting views

3 custom views

Administrators can define JSON properties and SQL column names in Data model, then regenerate these views for reporting on retained raw events:

  • analytics_custom_events_v1: all event names in both privacy modes.
  • analytics_custom_pageviews_v1: page views only.
  • analytics_custom_goals_v1: events with a retained goal.

Modeled scalar properties become text columns, and numeric properties can also get a number column for calculations. Missing values, JSON null, arrays, and objects produce SQL null. These views expose individual rows without suppression and need separately approved raw-data access. They include raw rows already marked as archived while those rows remain retained; archive aggregate cells do not contain the custom properties.

The bi_anonymous_* views keep their existing dimensions and disclosure thresholds. Deleting raw events removes their custom properties from reporting, even when archived counts remain available.

Underlying schema

Private source tables

Internal tables
These tables explain how the views are built; they are not the routine BI contract. The private events table contains individual rows and may contain enhanced identifiers or properties. Even anonymous-mode rows can remain personal data in context.

events

This is the unified event store. Logical types are shown because the exact physical SQL type varies across PostgreSQL, MySQL, MariaDB, SQL Server, and SQLite.

Logical columns in the private events table
Column Logical type Mode and purpose
idIntegerGenerated primary key for an individual event row.
website_tokenString (191)Required site key for both modes.
event_nameString (191)Required event name; defaults to view.
urlTextRequired stored page path. Exposed as page_path by the hourly view.
referrerNullable textStored referrer channel; exposed as referrer_channel.
privacy_modeString (20)Optin Model Analytics writes anonymous or enhanced; defaults to anonymous.
device_classString (20)Coarse device category for both modes.
viewport_bucketString (20)Coarse viewport category for both modes.
geo_areaNullable string (16)Optin Model Analytics writes only canonical continent:XX or country:XX values; synthetic other values exist only in the geography view.
generalized_user_agentNullable string (191)Enhanced only; forced to null in anonymous mode.
screen_widthNullable integerEnhanced only; forced to null in anonymous mode.
visitor_idNullable string (191)Enhanced only; forced to null in anonymous mode.
session_idNullable string (191)Enhanced only; forced to null in anonymous mode.
consent_stateNullable string (20)granted for enhanced rows; null in anonymous mode.
custom_dataNullable JSONScalar event properties and org_internal_traffic, true or false on every standard-profile event. Anonymous mode retains only explicitly configured consent-free properties and that flag. The bi_anonymous_* views and archives omit this JSON.
goal_eventNullable string (191)Enabled allowlisted goal code; present in anonymous mode only when its definition permits anonymous use.
created_atUTC datetimeUTC hour boundary for anonymous rows; exact server event time for enhanced rows.

BI disclosure thresholds

Deployment configuration keys in config/aggregate*.yaml supply the disclosure thresholds used by the three BI views.

Configuration keys for BI disclosure thresholds
Configuration key Logical type Meaning
anonymous_min_cell_countIntegerHourly event and daily goal threshold; default 5, allowed range 2–1000.
anonymous_geo_min_cell_countIntegerDaily geography threshold; default 25, allowed range 10–1000.

Saving BI disclosure settings rewrites configuration and regenerates the BI views. Environment overrides still take precedence.

There is no relational websites table. Website names, domains, and tokens live in config/websites.yaml. If reports need friendly names, maintain a controlled token-to-name mapping in the visualization layer or in a separately governed reporting dataset.

Recipes & Patterns

Safe query patterns

SQL recipes

Replace the example token with a parameter supplied by your BI tool. These queries select explicit columns and label results as released counts because enhanced events and unreleased counts are absent. Goal results are occurrence counts rather than unique converters, and geography counts may instead contribute to a released other pool.

Released page views by hour

SELECT
    event_hour,
    page_path,
    SUM(event_count) AS released_page_view_count
FROM bi_anonymous_events_v1
WHERE website_token = 'replace-with-site-token'
  AND event_name = 'view'
GROUP BY event_hour, page_path
ORDER BY event_hour, page_path;

Released goals by day

SELECT
    event_day,
    goal_event,
    event_count AS released_goal_occurrence_count
FROM bi_anonymous_goals_v1
WHERE website_token = 'replace-with-site-token'
ORDER BY event_day, goal_event;

Released page views by area

SELECT
    event_day,
    geo_area,
    SUM(event_count) AS released_page_view_count
FROM bi_anonymous_geo_events_v1
WHERE website_token = 'replace-with-site-token'
  AND event_name = 'view'
GROUP BY event_day, geo_area
ORDER BY event_day, geo_area;
Use SUM(event_count), not COUNT(*). Counting rows counts released grouped cells rather than the events represented by those cells.

Production checklist

Connection checklist

Setup guide

PostgreSQL, MySQL, MariaDB, or SQL Server

  • Create a dedicated database principal; never reuse the application's credentials.
  • Grant only the minimal connection/schema access required plus SELECT on the approved view or views needed; do not grant source-table SELECT. Grant statements for each database (opens in a new tab).
  • Use TLS and restrict the principal to the BI service's expected network location.
  • Review and reapply view grants after upgrades because migrations may recreate the views.
  • A shared view exposes every website token in it; BI filters are not tenant isolation.

SQLite

  • SQLite has no database users or object-level view grants.
  • A read-only database file still allows access to the private events table.
  • Have a trusted process export explicit columns from the approved views.
  • Protect exported files and use a server database when direct BI access is required.

Model and refresh settings

  • Configure event_hour as UTC; the physical timestamp may not carry a timezone marker.
  • Connector-specific physical types vary; model event_day as a date and event_count as a whole number.
  • Expect new hourly event data only after an hour completes, and goal and geography data only after a UTC day completes.
  • Treat missing cells as withheld or unavailable, not as confirmed zero traffic.
  • Remember that event_count counts event or goal occurrences, not visitors, unique people, or unique converters.
  • Review stored BI extracts separately; later threshold changes do not remove earlier exports.
Suppression reduces disclosure risk but does not establish k-anonymity or guarantee that released data is legally anonymous. Apply appropriate retention, access controls, and review to the visualization tool and its extracts.