Data Model
Every table and stream that enters Vendo is classified as one of a small set of entity types. These types define how data is stored, joined, transformed, and sent to destinations.
Think of them as the vocabulary Vendo uses before anything else happens — identity resolution, calculated properties, segments, attribution, and destination syncs all build on top of these entities.
The five entity types
| Entity | What it represents | Typical sources |
|---|---|---|
| Event | Something that happened at a point in time | Web tracking, Shopify orders, Stripe charges, email sends |
| User | A person or account record with traits | CRM contacts, analytics user profiles, Shopify customers |
| Group | A non-person entity with its own ID and properties | Companies, products, deals, CSV uploads, product feeds |
| Ad data | Platform-reported advertising metrics by day/campaign/ad | Google Ads, Meta Ads, TikTok Ads, LinkedIn Ads |
| Custom | Warehouse tables not yet classified | BigQuery tables connected before entity mapping is configured |
Once a custom table is mapped, it is promoted to event, user, group, or ad_data and behaves like any other entity of that type.
How the layers fit together
Layer 1 — Entities (this doc)
event · user · group · ad_data · custom
Layer 2 — Computed concepts
Calculated properties · Segments · Custom metrics · Data Studio transformations
Layer 3 — Destination outputs
Audiences · Conversions (platform-specific shapes sent to ad tools and CDPs)Layer 1 is raw, classified data. Layer 2 derives new fields and objects from Layer 1. Layer 3 renders computed results into the format each destination expects (for example, a Meta offline conversion payload or a Klaviyo audience list).
Audiences and conversions are not Layer 1 entities — they exist only at the destination side of the pipeline.
Events
An event is a time-stamped record of something that occurred: a page view, purchase, email open, subscription renewal, or any custom action you track.
Canonical fields
| Field | Required | Description |
|---|---|---|
event_name | Yes | Name of the event (e.g. purchase, page_view, email_sent) |
timestamp | Yes | When the event occurred |
user_id | No | Actor who triggered the event. Some system or ad events have no user. |
group_id | No | Group the event belongs to, if any (e.g. organization or account) |
idempotency_key | No | Provider-supplied key used to deduplicate ingest |
properties | No | Free-form payload — amounts, product lists, UTM params, email body, etc. |
What counts as an event
Vendo folds several legacy concepts into event:
- Page views and custom track calls from Web Tracking and the Tracking SDK
- Commerce events such as orders, checkouts, and refunds from Shopify or Stripe
- Email messages — the message metadata and body live in
properties - Revenue moments — purchase and charge semantics are expressed as events; aggregate revenue metrics are computed in Layer 2
Example
{
"event_name": "purchase",
"timestamp": "2026-05-20T14:32:00Z",
"user_id": "cust_abc123",
"properties": {
"order_id": "ord_987",
"currency": "USD",
"amount": 129.99,
"products": [{ "sku": "SKU-001", "quantity": 1 }]
}
}In the app
- Browse event tables in the Data Catalog
- Configure first-party collection under Data → Web Tracking
- Map source columns to the event schema via semantic mappings on each source
Users
A user is a person-level record with stable identifiers and traits. After Identity Resolution runs, records resolve to a single Vendo profile ID (vendo_user_id).
Canonical fields
| Field | Required | Description |
|---|---|---|
user_id | Yes | Canonical user identifier (resolved Vendo profile ID when available) |
email | No | Email address (PII) |
phone | No | Phone number (PII) |
first_name | No | Given name |
last_name | No | Family name |
organization | No | Company or organization; may link to a group record |
address | No | Structured postal address |
custom_properties | No | Source-specific traits (lifecycle stage, lead source, plan tier, …) |
updated_at | No | When the record was last updated |
What counts as a user
- User properties from analytics tools (Mixpanel, Amplitude, Segment)
- CRM contacts from HubSpot, Klaviyo, Customer.io
- Commerce customers from Shopify or Stripe
identifycalls from Web Tracking that attach traits to a known ID
Contacts and user properties are the same entity in the current model — one user row per person, merged across sources by the identity graph.
In the app
- View unified profiles under Data → Customers
- Configure identifier mappings on the Identity Resolution tab
- See resolved traits in Customer 360
Groups
A group is any non-person entity identified by a group_id and a type discriminator. Groups generalise what used to be separate concepts like company records, product catalog rows, and lookup tables.
Canonical fields
| Field | Required | Description |
|---|---|---|
group_id | Yes | Stable identifier for this group instance |
type | Yes | Kind of group — see common types below |
name | No | Human-readable label |
custom_properties | No | Type-specific attributes |
updated_at | No | When the record was last updated |
Common group types
| Type | Use for |
|---|---|
company | B2B accounts, organizations |
product | Product catalog items, SKU feeds |
deal | CRM deals or opportunities |
list | Static or uploaded reference lists |
| Custom slugs | Any group type registered in your workspace |
CSV uploads and warehouse tables that were previously treated as lookup tables are now registered as groups with an appropriate type.
Product feed destinations (Google Merchant Center, Meta catalog sync) consume groups where type = product.
In the app
- Manage group types and instances under Data → Groups
- Upload CSV reference data — it registers as a group with a chosen type
- Map warehouse columns to the group schema via semantic mappings
Ad data
Ad data is platform-reported advertising performance: spend, impressions, clicks, and conversions broken down by date, campaign, ad set, and ad. It is structurally different from events — one row per day per ad entity, not one row per user action.
Canonical fields
| Field | Required | Description |
|---|---|---|
date | Yes | Reporting date |
campaign_id | No | Platform campaign identifier |
campaign_name | No | Campaign display name |
ad_group_id | No | Ad set / ad group identifier |
ad_id | No | Individual ad identifier |
spend | No | Amount spent in account currency |
impressions | No | Impression count |
clicks | No | Click count |
conversions | No | Platform-reported conversion count |
attribution_keys | No | UTM tags, click IDs (gclid, fbclid), geo — used to join ad data to events at query time |
How ad data joins to events
Ad platforms do not share user-level click streams with your warehouse. Vendo joins ad data to events using attribution keys stored on both sides:
- Events carry UTM parameters and click IDs in
propertiesor dedicated columns. - Ad data rows carry the same keys in
attribution_keys. - Attribution models and reports use these joins to attribute conversions to channels and campaigns.
Typical sources
In the app
- Connect ad platforms under Data → Apps
- Review synced campaign tables in the Data Catalog
- Use ad data in Reports, Optimization, and attribution transformations
Custom (unclassified)
Custom is a staging state for warehouse-connected tables that have not yet been assigned an entity type. It is not a permanent classification — every custom table should eventually be promoted.
When data is custom
- You connect a BigQuery source or upload a CSV and have not finished semantic mapping
- A table’s shape does not obviously match event, user, group, or ad data yet
- You are exploring raw warehouse data before deciding how it fits the model
Promoting custom to a real entity
- Open the source in Data → Apps.
- Open Semantic mapping for the table.
- Choose the target entity: Event, User, Group, or Ad data.
- Map source columns to the canonical fields for that entity.
- Save and run a sync — the table is reclassified and participates in identity resolution, transformations, and destinations like any native source.
For groups, also select the group type (product, company, etc.) during mapping.
Entity relationships
┌─────────────┐
│ ad_data │
│ (by date/ │
│ campaign) │
└──────┬──────┘
│ attribution_keys
▼
┌──────────┐ user_id ┌──────────┐ group_id ┌──────────┐
│ user │◄────────────│ event │─────────────►│ group │
│ (traits) │ │ (actions)│ │ (things) │
└──────────┘ └──────────┘ └──────────┘
▲ │
│ identity graph │
└──────────────────────────┘- Events reference a user (who) and optionally a group (what account or product context).
- Users accumulate traits from every source mapped as
user. - Groups stand alone but can be linked from events and users.
- Ad data joins to events for attribution; it does not replace event-level conversion tracking.
Legacy names you may still see
Vendo is consolidating an older eight-type model into the five entities above. During the transition, you may encounter legacy labels in older integrations or documentation:
| Legacy term | Current entity |
|---|---|
events | Event |
user_properties, contacts | User |
group_properties, lookup_table | Group |
ad_data | Ad data (unchanged) |
email_messages, revenue | Event (semantics in properties or Layer 2 metrics) |
custom | Custom (unclassified) |
audiences, conversions | Layer 3 destination outputs — not source entities |
New sources and mappings should always use the current entity names.
Related documentation
- Data Catalog — browse classified tables and columns
- Web Tracking — collect first-party events
- Customer 360 — unified user profiles from
userentities - Identity Resolution — stitch users across sources
- Data Studio transformations — compute metrics and segments from entities
- Pipelines — how sources, transformations, and destinations connect
- Attribution models — join events to ad data