Data schema

Attribution collects a number of data points that can be exported. Data scientists, analysts and developers can use the schema below to design custom reports on top of this data.

The export holds the same data, in the same form, that your dashboard uses to build every report. It is raw, as collected, with no attribution model applied.

Modeled data cannot be exported, because the model depends on what you select on the dashboard at the time: the filters, the date range, the model and the attribution type. The export contains no pre-built reports either.

The diagram below shows how the raw data is laid out. Some tables hold normalised data, so the same information can appear in two tables in different forms: the params table stores the URL parameters parsed out of the events.uri column.


Events

The events table contains every event and pageview from the track() and page() calls, and every event sent server-side. Event properties are not stored here but in the properties table.

Column NameTypeDescription
idBIGINTUnique ID of the pageview or track event. Primary key, referenced from other tables.
nameVARCHARThe event name. Pageviews are Loaded a Page or Viewed * Page, depending on how you call page().
ipVARCHARIP address of visitor.
created_atTIMESTAMPWhen the event was written to the Attribution database.
user_idBIGINTReferences users.id. This is the internal ID generated by Attribution, not the userId you pass when tracking.
browser_idBIGINTReferences browsers.id.
project_idBIGINTInternal ID of your project.
timeTIMESTAMPWhen the event happened.
referring_urlVARCHARHTTP Referer URL of the pageview.
referring_hostVARCHARHostname part of referring_url.
revenueBIGINTRevenue property value in cents.
visitor_idBIGINTInternal ID of visitor. References visitors.id.
updated_atTIMESTAMPWhen the event was last updated.
uriVARCHARPageview URL at capture time.
uri_pathVARCHARURL path of uri.
self_referralBOOLEANTRUE if referring_host matches uri hostname, FALSE otherwise.
message_idVARCHARUnique event ID generated by the library that sent the event.
sourceVARCHARName of the source the event was captured from.
typeVARCHARp for pageviews from page(), t for events from track().

Visits (touchpoints)

The visits table is named after our internal ID for your project: if that ID is 1234, the table is visits_1234. It contains everything you see as Visits on the dashboard.

Visits are touchpoints: a visit is the first pageview of a session; see Visits and visitors. We do not call them sessions, because a session is the whole group of pageviews and events, while the visit is only the first pageview, the one that carries the source of the session in its URL parameters, its referrer or both. The source of a visit, and of the session that follows it, is called a filter in Attribution.

🚧

Heads up!

This table holds the most valuable information for building custom reports on Attribution's data.

Unlike most other tables, this one is NOT exported incrementally: drop the existing visits table and load it in full with every export. Visits depend on filters, and filters are added, changed and removed all the time, by integrations and by your marketing team, so the visits table is rebuilt from the events and filters tables on every run.

In other words, visits are the result of applying filters to events: if a filter has the rule "URL parameter utm_source is partner", every pageview with utm_source=partner in its URL is written to the visits table.

Column NameTypeDescription
idBIGINTReferences events.id. Unique within visits, usable as the primary key.
visitor_idBIGINTReferences events.visitor_id and visitors.id.
visit_timeTIMESTAMPReferences events.time.
filterBIGINTReferences xNNNN_filters.id where xNNNN_filters.type is filter. Replace NNNN with your internal Project ID, e.g. 1234.
company_idBIGINTPositive: equals visitor_id. Negative: the visitor belongs to a company (has a company_id trait) and the absolute value references companies.id.
visit_typeVARCHARv for a regular visit (pageview); i for an influence touchpoint used by TV attribution.
user_idBIGINTReferences events.user_id and users.id. Can be NULL.
original_created_atTIMESTAMPMatches visitors.original_created_at: the createdAt trait, if it was passed to identify(). Can be NULL.
pathVARCHARMatches events.uri_path, except that NULL stands for no path or /.

Most columns in visits are redundant; they are there to make complex queries cheaper. You can load only the two that matter, id and filter, and get the rest by joining events and the other tables.

Visit costs

The table is named visits_1234_costs for internal project ID 1234. It spreads each filter's daily spend over the visits of that day, which is how the dashboard arrives at a cost per visit. Like visits, it is rebuilt and loaded in full on every run.

Column NameTypeDescription
cpvFLOATCost per visit: amount divided by visit_count.
visit_countINTEGERNumber of visits the filter had on the date.
visit_dateDATEThe date.
filterBIGINTReferences xNNNN_filters.id where type = 'filter'.
amountINTEGERSpend for the filter on the date, in cents, from amounts.

Filters

The filters table is named after our internal ID for your project: if that ID is 1234, the table is x1234_filters_v2. It contains everything you see as filters and channels on the dashboard.

The dashboard is a tree, the "filter tree". The filters table holds both channels (type = 'group') and filters (type = 'filter'), flattened, because trees are awkward in SQL.

When joining this table to visits, join only rows WHERE "type" = 'filter'. That gives filter-level reporting; for channel-level reporting, sum the filter metrics by channel, grouping on top_parent_id, parent_id or channel_N_id.

🚧

Important notes

This table is NOT exported incrementally: filters are added, changed and removed often, by integrations and by your marketing team, so truncate it and load it again on every run.

The primary key is the composite (id, type): two rows can share an id without being duplicates.

Groups you create on the dashboard, such as Paid Traffic, are not in this table.

Column NameTypeDescription
idBIGINTID of filter or channel. Not unique.
nameVARCHARName of the filter or channel.
typeVARCHARCould be filter (Filter) or group (Channel).
labelVARCHARA qualifier set by Attribution or an integration that says what the row is about. Can be NULL.
integrationVARCHARTechnical identifier of the integration, e.g. bing for Microsoft Advertising.
parent_idBIGINTReferences id where type is group: the parent channel of this row.
top_parent_idBIGINTReferences id where type is group: the top-level channel of this row.
ordinalINTEGERPosition of the row inside its parent channel.
levelINTEGERNesting distance from the root level.
sort_indexINTEGERPosition of the row in the whole tree. ORDER BY sort_index gives the dashboard's order.
pathVARCHARJSON array of the channel names from the top of the tree down to this row.
channel_N_nameVARCHARName of the channel at level N of the tree above this row.
channel_N_idBIGINTID of the channel at level N of the tree above this row.
path_level_NVARCHARThe channel names down to level N, joined by >>. Does not include the row's own name.
📘

channel_N_name, channel_N_id and path_level_N exist for levels 1 to 5 of the tree. If your tree is deeper than five levels, use the path column, which holds the full hierarchy.

To see the dashboard's tree from the exported filters table:

SELECT
    repeat('▷ ', level) || name,
    *
FROM
    xNNN_filters_v2
ORDER BY
    sort_index;

Filters (legacy, pre-2024)

New exports use the format above. Projects whose export was set up before 2024 receive this legacy format.

The legacy table is named x1234_filters for internal project ID 1234, and holds the same channels and filters.

The dashboard is a tree, the "filter tree". The filters table holds both channels (type = 'group') and filters (type = 'filter'), flattened, because trees are awkward in SQL.

Join it to visits on rows WHERE "type" = 'filter' only, and group on top_parent_group_id or parent_group_id for channel-level reporting.

🚧

Important notes

Like the current format, this table is truncated and loaded in full on every run, and its primary key is the composite (id, type). The dashboard's order is not preserved in the legacy format.

Column NameTypeDescription
idBIGINTID of the filter or channel. Not unique.
parent_group_idBIGINTReferences id where type is group: the parent channel of this row.
top_parent_group_idBIGINTReferences id where type is group: the top-level channel of this row.
typeVARCHARCould be filter (Filter) or group (Channel).
nameVARCHARName of the filter or channel.
labelVARCHARA qualifier set by Attribution or an integration that says what the row is about. Can be NULL.
integrationVARCHARTechnical identifier of the integration, e.g. bing for Microsoft Advertising.
pathVARCHARJSON array of the channel names from the top of the tree down to this row.

Visitors

A row is added every time a new visitor is tracked or identified, so the table holds both anonymous and identified visitors.

When an anonymous visitor later identifies, Attribution keeps two rows: the anonymous visitor, and the visitor it was migrated_to. The userId you pass to identify() is not in this table; it is in users. Projects on split identity mode lay these rows out differently; see Classic vs Split Mode Identity.

Column NameTypeDescription
idBIGINTUnique ID. Primary key, referenced from other tables.
user_idBIGINTReferences users.id.
browser_idBIGINTReferences browsers.id.
project_idBIGINTInternal ID of your project.
updated_atTIMESTAMPWhen the visitor was last updated.
ipVARCHARVisitor IP address.
traitsVARCHARJSON hash of visitor traits.
emailVARCHAREmail extracted from traits.
company_idBIGINTReferences companies.id: the company the visitor belongs to.
migrated_toBIGINTReferences id of the visitor this one was aliased or merged into.
original_created_atTIMESTAMPWhen the visitor registered in your own system: the createdAt trait, extracted from traits.

Users

The identifier column is the userId you pass in identify() and track() calls: the users your tracking identified, whether through Segment, Shopify, Heap or the snippet.

Column NameTypeDescription
idBIGINTUnique ID. Primary key, referenced from other tables.
identifierVARCHARThe userId you passed in identify() or track(): your own user ID.
created_atTIMESTAMPWhen the user was first written to the Attribution database.
project_idBIGINTInternal ID of your project.
updated_atTIMESTAMPWhen the user was last updated.
original_created_atTIMESTAMPDEPRECATED. Use visitors.original_created_at instead.

Amounts

Spend, from the ad integrations and entered by hand. One row is the spend for one filter on one date. This is the Spend column of the dashboard.

Column NameTypeDescription
idBIGINTUnique ID. The primary key for this table.
valueBIGINTSpent amount in cents.
created_atTIMESTAMPWhen the amount was first written to the Attribution database.
filter_idBIGINTReferences visits.filter, and xNNNN_filters.id where type = 'filter'.
sourceVARCHARIntegration name, if the amount was pulled automatically.
dateDATEDate the spend is for.
updated_atTIMESTAMPWhen the amount was last updated.
project_idBIGINTInternal ID of your project.
amount_range_idBIGINTInternal use. References the amount range this spend is part of, if any.
deletedBOOLEANWhether the spend was deleted. Always add deleted = FALSE when you query or join this table.
original_valueBIGINTThe original value in cents, if currency conversion was applied.
original_currencyVARCHARISO code of the original currency, if currency conversion was applied.
conversion_rateDECIMAL(18, 6)Conversion rate applied.
currency_converted_atTIMESTAMPWhen the currency was converted.

Impressions

Ad impressions and clicks reported by the ad integrations, one row per filter per date. Like amounts, this table is merged incrementally.

Column NameTypeDescription
idBIGINTUnique ID. The primary key for this table.
valueBIGINTNumber of impressions.
filter_idBIGINTReferences visits.filter, and xNNNN_filters.id where type = 'filter'.
sourceVARCHARIntegration the row was pulled from.
dateDATEDate the impressions are for.
updated_atTIMESTAMPWhen the row was last updated.
project_idBIGINTInternal ID of your project.
deletedBOOLEANWhether the row was deleted. Add deleted = FALSE when you query or join.
clicksBIGINTNumber of clicks.

Params

The URL parameters of pageviews, parsed out of events.uri, one row per key and value. A pageview with the uri https://example.com/?utm_source=partner&utm_campaign=AWESOME adds two rows: utm_source = partner and utm_campaign = awesome. Keys and values are lowercased and truncated, keys to 32 characters and values to 128. For the original values use events.uri.

Column NameTypeDescription
keyVARCHARParameter key.
valueVARCHARParameter value.
project_idBIGINTInternal ID of your project.
timeTIMESTAMPWhen the event happened. Matches events.time.
event_idBIGINTReferences events.id.
updated_atTIMESTAMPWhen the parameter was last updated.
idBIGINTUnique ID. The primary key for this table.

Properties

The properties passed with track() calls, one row per key and value. track('Custom Event', { plan: 'Starter', revenue: '$50.00' }) adds one row to events and two rows here: plan = Starter and revenue = $50.00. Keys and values are stored as passed, with no transformation.

Column NameTypeDescription
idBIGINTUnique ID. The primary key for this table.
keyVARCHARProperty key.
valueVARCHARProperty value.
project_idBIGINTInternal ID of your project.
event_idBIGINTReferences events.id.
updated_atTIMESTAMPWhen the property was last updated.

Companies

The companies visitors belong to. A visitor is assigned to a company by the company_id and company_name traits passed to identify().

Column NameTypeDescription
idBIGINTUnique ID. The primary key for this table.
project_idBIGINTInternal ID of your project.
identifierVARCHARThe company_id trait you passed to identify(): your own company ID.
updated_atTIMESTAMPWhen the company was last updated.
nameVARCHARThe company_name trait passed to identify().
traitsVARCHARJSON hash of company traits.

Browsers

The visitor's browser (user_agent) and anonymous ID (cookie_id). On a visitor's first pageview the snippet generates a unique anonymous ID, stores it in the browser and sends it with every call, page(), identify(), track() and alias().

Column NameTypeDescription
idBIGINTUnique ID. The primary key for this table.
cookie_idVARCHARThe anonymous ID: the value the snippet stores in the browser under the _attrb key. Attribution.user().anonymousId() returns it in JavaScript.
created_atTIMESTAMPWhen the browser was first written to the Attribution database.
user_agentVARCHARThe browser's User-Agent header, for detecting platform and device.
updated_atTIMESTAMPWhen the browser was last updated.
project_idBIGINTInternal ID of your project.

If you have any questions, write to [email protected].