Meerkats AI

The semantic layer

How every metric is defined once, named per platform, checked before use, and traced to its source.

The semantic layer is where every metric is defined once. ROAS, CAC, AOV, days of cover: each has one definition, one set of inputs and one set of dimensions it can be broken down by, and every report, chart, agent answer and rule uses that same definition. Change it in one place and everything that uses it changes with it.

It also carries the rule that makes the numbers trustworthy: the model never does arithmetic. When an agent needs ROAS for last week by campaign, it requests that metric from the semantic layer and gets back a computed result with its source attached. It does not add up spend and revenue itself. Every figure you see is the output of a recorded query you can re-run.

How a metric is defined#

A metric definition has four parts.

PartWhat it isExample: ROAS
MeasuresThe raw quantities it's built fromAttributed revenue and ad spend
FormulaHow the measures combinerevenue ÷ spend
DimensionsWhat it can be grouped or filtered byPlatform, campaign, day, week, product
BasisWhich attribution and window the measures use, and which platform's label appliesPlatform-reported (7-day click) or corrected (click ID to order)

The metric catalog lists every metric in your workspace with the dimensions it can be grouped by. In the app, the info icon on Cockpit opens About these numbers, which shows the glossary of metrics on that screen with their definitions. Over the API, GET /metrics/catalog returns the same catalog as a map of metric name to dimension names.

GET /metrics/catalog
{
  "success": true,
  "vertical": "ecommerce",
  "metrics": {
    "total_revenue": ["order__channel", "metric_time__day"],
    "roas": ["ad_row__platform", "metric_time__week"],
    "real_roas": ["ad_row__platform", "ad_row__campaign", "metric_time__day"]
  }
}

A dimension name reads as entity__attribute: ad_row__platform is the platform of an ad row, metric_time__week is the week the metric is measured in. Unknown metric or dimension names are rejected rather than guessed.

One metric, each platform's own name#

Platforms name the same idea differently, measure it on different attribution bases, and sometimes invert it. Meerkats keeps one canonical metric underneath and shows each platform’s own label on that platform’s tables, with the formula and basis in the tooltip. A term that is derived or blended carries a derived badge and its printed formula. You will never see ACOS on a Meta table, or ROAS as a Google column.

PlatformIts labelWhat it means
Meta AdsPurchase ROAS, Website purchase ROASA multiplier. Depends on the ad set's attribution setting; includes modelled conversions without flagging them.
Google AdsConv. value / costA decimal ratio. Google has no column named ROAS. The Target ROAS setting displays as a percentage.
Flipkart AdsROIA multiplier that blends clicks and views, direct and indirect revenue. Meerkats also shows the direct split.
Amazon AdsACOS, ROASACOS is spend ÷ sales as a percentage, so lower is better. ROAS is 100 ÷ ACOS.
BlendedMERRevenue ÷ ad spend across the whole business. Meerkats prints the formula direction next to it, because conventions differ.

Attribution bases#

The basis is part of the definition. Two numbers with different bases are different metrics, even with the same name.

PlatformDefault basis
MetaSet per ad set. The default is 7-day click plus 1-day engaged view plus 1-day view.
Google AdsConversions credited to the click date, with up to about 90 days of lag.
FlipkartLast touch: 28-day click or 7-day view. PCA uses a 7-day last-view model that is never blended with PLA.
AmazonSponsored Products 7-day click; Sponsored Brands 14-day click; Sponsored Display 14-day click plus modelled view.
ShopifyLast non-direct click, 30 days, click only.

Platform-reported and corrected#

Every ad platform attributes generously: view-through conversions, long windows, modelled events. Add up each platform’s reported conversions and the total is more than the orders you actually received. So Meerkats keeps two tiers of truth and shows both.

  • Platform ROAS: what the platform reports, on its own basis. Shown as the platform’s own label.
  • Real ROAS (corrected): revenue from your own orders, matched to the click that brought them in by click ID (gclid, fbclid, ttclid) or UTMs, divided by billed spend. Needs a connected sales source such as Shopify.
  • ROAS gap: platform ROAS ÷ real ROAS. How much the platform over-claims. A gap that jumps is a tracking or model change, not a performance change.

Marketplaces are different: on Amazon or Flipkart the order happens on the platform, so there is no click ID path. There, the truth tier is the marketplace’s own order data (from the Seller API), which is stronger than any pixel. What stays unobservable is paid-versus-organic cannibalisation, so Meerkats names it as an open question and uses share-of-voice and halo proxies rather than inventing a number.

Platforms are never ranked on their own ROAS

Cross-platform comparison uses the click-ID spine where it exists and marketplace orders where it doesn’t. The only metric comparable across both is blended MER (or blended contribution margin) at workspace level, from the order ledger. Allocation decisions rank platforms on marginal blended contribution, never on one platform’s reported ROAS against another’s.

Blended metrics#

Some metrics belong to no platform. Revenue comes from your order ledger by order date; spend comes from each platform’s billing. These are defined in the semantic layer too, and a blended tile always prints its formula.

MetricDefinition
MERTotal revenue ÷ total ad spend, whole business
TACOSTotal ad spend ÷ total revenue, as a percentage
Blended CACTotal ad spend ÷ new customers
New-customer ROASRevenue from first-time customers ÷ total ad spend
CM1, CM2, CM3Net revenue minus product cost; minus fees, shipping and RTO; minus marketing
LTVCumulative gross profit per customer at 30, 60, 90 and 180 days, by cohort
CAC paybackMonths for a customer's gross profit to repay their acquisition cost
Repeat rateShare of a cohort placing a second order within N days
RTO rateReturn-to-origin orders ÷ COD orders
Days of coverUnits on hand ÷ average daily units sold
OOS ad wasteLive ad spend on products that are out of stock or at risk

Money is always in currency units in your workspace currency. Conversions from a platform’s currency are stamped with the rate and its date. Minor units (paise, cents) never appear in a report.

Checks that run before any conclusion#

A metric can be defined correctly and still mislead. Before an agent draws a conclusion, the semantic layer runs measurement checks and reports a failed check as a measurement finding, never as a performance finding.

CheckWhat it guards against
FreshnessWhen each table last synced, quoted verbatim. Stale data stops the analysis and says so.
Attribution lagConversions accrue for days after the click. The last one to three days always understate, and the current window is hit harder than the comparison window. Spend with zero conversions younger than three days is lag, not failure.
DedupePlatforms synced through two paths are deduplicated before anything is summed.
Snapshot grainCumulative reports are read at their latest date only, never summed across snapshots.
Metric model changeWhen a platform silently redefines a metric (a new attribution model, a changed eligibility gate), a step change on that date is a model change, not a business change. Meerkats keeps a per-platform changelog and rebaselines across it.
Date alignmentDays follow each account's time zone; cross-platform ranges use explicit bounds.
Temporal confoundsA new listing's boost wearing off, or a learning-phase dip, looks like a bad change. Meerkats checks age and edit dates before blaming a lever.

A check that could not run says so. “No issue detected” without a query behind it never appears.

Where every number comes from#

Every value an agent shows carries a source label, visible by default rather than behind a toggle. One click goes from the value to its reference: the query text, the evidence row, or the platform read.

LabelMeaning
computedFrom a recorded query on your synced data. Deterministic: run it again and you get the same number.
liveRead from the platform just now, with the read time. Wins over the warehouse when they disagree, and flags a re-sync.
yoursSomething you told Meerkats, for example a target or a budget cap. Locked; never re-derived.
your setupHow your account does things: naming conventions, campaign structure, geo pattern.
your dataMeasured on your account: baselines, winners, placement and geo weights, with the window and date.
rulePlatform knowledge: what a setting means, which cause produces which symptom.
from auditA finding from an earlier investigation, with a link to its evidence.
suggestedComputed from the above, for example a starting budget from learning-phase maths. Shows its inputs.
you choseYour answer to a question the agent asked. Also logged as a gap in the defaults.
scrapedA web snapshot (competitor tracking) with its date and coverage. No mid-cycle claims.
AI-judgedA model's judgement over raw data, for example review themes. Always labelled; never blended with facts.

When two sources disagree, for example the warehouse and a live read, or pixel purchases and real orders, Meerkats surfaces the disagreement as a finding with both values. It never silently picks one. A value with no source does not render at all.

Query the semantic layer#

Agents query it for you. You can also query it directly: from the chat box on Cockpit, from Claude over the MCP server, or from your own code over the API. All three hit the same definitions.

curl -X POST https://partners.meerkats.ai/api/public/v1/metrics/query \
  -H "X-API-Key: mk_live_…" \
  -H "Authorization: Bearer <user token>" \
  -H "X-Workspace-Id: <workspace id>" \
  -H "Content-Type: application/json" \
  -d '{
    "metrics": ["roas", "real_roas", "campaign_spend"],
    "groupBy": ["ad_row__platform", "metric_time__week"],
    "dateRange": "last_30_days",
    "orderBy": ["-campaign_spend"],
    "limit": 50
  }'

dateRange accepts presets (today, yesterday, last_7_days, last_30_days, last_90_days, this_week, last_week, this_month, last_month, this_year, all) or a rolling last_N_days; startDate and endDate override it. Rows carry one key per requested metric and dimension. The example figures above are illustrative.

What you can change#

  • Add your own metrics and definitions, for example a contribution margin that uses your real fee schedule.
  • Set the cost per item, fees and shipping assumptions the margin ladder uses, so CM1 to CM3 reflect your business.
  • Choose which basis a report shows by default: platform-reported, corrected, or both side by side.

Definitions you change are versioned. A report from last month still shows the definition that was in force then, and says so.

See also metric definition and state in the glossary.

Last updated October 2, 2026