Data Ontology — The 10 Canonical Entities

A shared vocabulary for the objects Admaxxer models. Read this before querying /api/v1/*, before reading dashboard SQL, or before asking the Claude agent a "give me X by Y" question.

TL;DR: Admaxxer's data model is built on 10 canonical entities: Workspace, Website, Visitor, Session, Pageview, Goal, Payment, AdAccount, the Campaign / AdSet / Ad hierarchy, and Subscription. Workspace is the multi-tenant root; every other entity belongs to exactly one workspace. High-volume behavioral entities (Pageview, Goal, Payment, ad insights) live in the append-only analytics warehouse for fast aggregation; structural state (Workspace, User, AdAccount config) lives in the primary transactional database. IDs that come from a third-party system are always clearly marked as external. The same 10 entities surface unchanged in the UI, in /api/v1/*, and in every Claude agent tool call.

What is a data ontology — and why does it matter?

A data ontology is the shared vocabulary your stack uses to refer to the entities and relationships in your business. When the dashboard says "Revenue by Ad", the API says GET /api/v1/ads/{id}/revenue, and the Claude agent says get_ad_revenue(ad_id, ...) — they're all referring to the same Ad entity, related the same way to the same Payment entity. Drift in that vocabulary is one of the most common analytics-platform failure modes ("every team has its own definition of MRR").

Admaxxer publishes its ontology as a documented contract. Every entity name, every field name, every join key is listed here. If you're building against the API, integrating with a downstream BI tool, or asking the Claude agent a complex question, this is the source of truth.

The 10 core entities

1. Workspace — the multi-tenant root

The top-level container. Every other entity in the system belongs to exactly one workspace, which is how data is kept isolated. Every API request resolves to one workspace; cross-workspace queries are explicitly disallowed.

2. Website — a tracked property within a workspace

A single domain (or subdomain set) the merchant runs the pixel on. One workspace can have many websites — useful for brands running multiple Shopify stores, for agencies, or for staging-vs-production split.

3. Visitor — the anonymous identity

An anonymous identity tied to the visitor_id first-party cookie. Spans multiple sessions across cookie rotation when identify(external_user_id) has been called. Without an external_user_id, the visitor is bounded by the cookie's lifetime (24h by default; merchants can extend to 365 days on Pro+ plans).

4. Session — a discrete visit

A bounded sequence of pageviews and goal events from one visitor. Session boundary is a 30-minute idle gap (configurable per workspace).

5. Pageview — a single page render

One row per page render. The atomic unit of behavioral data.

6. Goal — a discrete user action

Any custom goal fired via admx.goal(), data-admx-goal="", or server-side POST /api/event. Plus the seven reserved __admx_* goals fired automatically by script.plus.js (outbound click, file download, form submit, video play, scroll-50, scroll-75, scroll-90).

7. Payment — a revenue event

The most heavily-deduplicated entity. Each row represents exactly one charge, refund, or subscription renewal — whether it landed via Custom Pixel, server-side webhook, or daily reconciliation poll.

8. AdAccount — a connected Meta/Google/TikTok account

One row per platform-credential set the merchant has connected.

9. Campaign / AdSet / Ad — the ad-platform hierarchy

The three-level hierarchy pulled from each ad platform's API. Stored as cached daily insights in the analytics warehouse, keyed by date plus the account, campaign, ad set, and ad.

10. Subscription — recurring revenue

A long-lived agreement between the merchant's customer and a recurring-billing provider. Stripe subscription, Recharge subscription, ReCharge-equivalent, Polar subscription, etc.

Entity relationships

The relationships between the 10 entities, written as cardinality + join key:

Workspace 1 ----- N Websites          (a workspace owns many tracked properties)
Workspace 1 ----- N AdAccounts        (a workspace owns many ad connections)
Workspace 1 ----- N TeamMembers       (a workspace has many members)
Workspace 1 ----- N Subscriptions     (a workspace has its own billing subscription)

Website 1 ------- N Visitors          (a property has many visitors)
Visitor 1 ------- N Sessions          (visits are split on a 30-min idle gap)
Session 1 ------- N Pageviews         (a visit has many page renders)
Session 1 ------- N Goals             (a visit has many goal completions)
Visitor 1 ------- N Payments          (a visitor has many revenue events)

AdAccount 1 ----- N Campaigns         (an account has many campaigns)
Campaign 1 ------ N AdSets            (a campaign has many ad sets)
AdSet 1 --------- N Ads               (an ad set has many ads)
Ad N ------------ M Goals             (attribution model: last-touch, linear, time-decay)
Ad N ------------ M Payments          (attribution model + cohort window)

Cardinality reads "1 ----- N" as "one [left] has many [right]". N ------ M denotes a many-to-many relationship resolved at read-time by the attribution model; there is no static join table.

Where the entities live (analytics warehouse vs primary database)

Analytics warehouse — append-only event data

The high-volume, append-only behavioral and revenue data lives here, where it can be aggregated fast:

Primary database — structural state

Everything with a lifecycle (created → updated → deleted) that you query transactionally lives here:

The split rule: if it's append-only and you query it analytically, it goes to the analytics warehouse. If it has lifecycle (created → updated → deleted) and you query it transactionally, it goes to the primary database.

Common metrics — how the entities combine

The highest-frequency cross-entity calculations inside Admaxxer, described by the entities they relate and the rule they apply:

Revenue by ad — last-touch attribution

Each Payment is matched to the Pageview that touched the same Visitor, and that pageview's UTM attribution links it to the Ad that drove the click. Revenue is then summed per ad over the chosen window (e.g. the last 30 days). The result answers "which ad produced this revenue?" under a last-touch model.

Cohort LTV — first-purchase 90-day window

Visitors are grouped into cohorts by the month of their first purchase. For each cohort, Admaxxer sums every subsequent Payment from those same visitors within a fixed window (e.g. 90 days), then divides by the number of visitors in the cohort to get lifetime value per visitor. This shows how much a cohort is worth as it matures.

MER — blended marketing efficiency ratio

For each day, Admaxxer takes total Payment revenue and divides it by total ad spend across every connected AdAccount (Meta + Google + …) for that same day. The ratio is your blended return on ad spend, independent of any single platform's self-reported attribution.

Pre-aggregated summary series make these instant on the dashboard; the per-entity model above is what they roll up. See /documentation/data/revenue-data-flow for the full ingestion model.

Naming conventions

Comparison — vs Triple Whale, vs Datafast

Different ontologies for different shapes of business:

Entity classTriple WhaleDatafastAdmaxxer
Multi-tenancy Account Workspace Workspace
Tracked property Shop (Shopify-only) Website Website (multi-domain)
Anonymous identity (none — Shopify customer ID required) Visitor Visitor (anonymous ID + logged-in user ID + hashed email)
Visit semantics (implicit through orders) Session + Pageview Session + Pageview
Discrete action Customer Journey event Goal Goal (custom + 7 reserved __admx_*)
Revenue event Order (Shopify-native) Payment Payment (deduplicated across 7 providers)
Customer Customer (with LTV, cohort, RFM) (none as first-class entity) (derived from Visitor + Payment)
Product Product (Shopify-native catalog) (none) (none in v1)
Marketing entity Marketing Entity (collapsed across platforms) (none) AdAccount + Campaign + AdSet + Ad (platform-native, three levels)
Operations Operations (fulfillment, COGS) (none) (none in v1)
Subscription Subscription (Recharge / Skio) Subscription Subscription (Stripe / Paddle / LS / Polar / Recharge via webhook)

The shape difference: Triple Whale's ontology is commerce-rich (10 entities including Order, Customer, Product, Operations) because TW's roots are pure-Shopify. Datafast's ontology is analytics-only (5 entities: Visitor, Session, Pageview, Goal, Payment) because DF doesn't model the ad stack. Admaxxer sits between: 10 entities including DTC commerce + first-class ad-platform hierarchy + the analytics primitives, but without commerce-fulfillment objects (Product, Inventory, COGS) that aren't in the v1 scope.

API access — same entities, programmatic surface

Every entity in this document is exposed unchanged through /api/v1/*. The endpoints follow REST plus a few RPC-shaped reads for analytical queries:

Authentication is by API key issued from /dashboard/site-settings; rate-limited per workspace. Usage is tracked against your plan's monthly event allowance. The full endpoint reference is at the developer documentation hub at /documentation/developer.

Inside the dashboard, the same entities surface in the Claude agents' tool definitions (see /documentation/architecture/how-data-works): list_campaigns reads the AdAccount + Campaign + AdSet + Ad hierarchy; get_revenue_summary reads the Payment entity; query_metric exposes the same POST /api/v1/metrics/query shape.

See also

How data works (architecture) · Revenue data flow · our analytics warehouse auth model · Multi-currency display · Metric glossary · Conversion funnels · Custom goals · GSC integration