Semantic LayerAnalytics Modeling Language

The programmable semantic layer.

A semantic layer that's built like a real programming language, not duct-taped together from YAML and SQL strings. So your business logic stays reusable, composable, and maintainable like real code.

AML ecommerce.aml
Model orders {
  table_name: 'ecommerce.orders'

  dimension status     { type: 'text' }
  dimension created_at { type: 'datetime' }

  measure revenue {
    type: 'number'
    definition: @aql sum(order_items.quantity * products.price) ;;
  }
}

# assemble models into a governed dataset
Dataset ecommerce {
  models: [orders, order_items, products, users]
  relationship(order_items.order_id > orders.id)
  relationship(order_items.product_id > products.id)
}
one definition · compiles to native SQL · governs every surface ↓
Semantic layer

Define your business once. Query it from anywhere.

Model your metrics, dimensions, and relationships in AML. It compiles to native SQL on your warehouse, and every consumer queries the same governed definitions.

Your warehouse Snowflake · BigQuery · Databricks · Postgres
push-down · no data movement
AML semantic layerdefined once · compiles to native SQL
Models & relationshipshow your tables join
Metrics & dimensionsrevenue, active users, cohorts
Business contextlabels, descriptions, formats
Access policiesrow-level security & permissions
queried by every consumer
Dashboards & self-serve

Drag-and-drop exploration and AI chat on governed datasets.

In-platformLearn more →
Embedded analytics

iframe & JS SDK, SSO/JWT, row-level security, white-label.

AI agents

Query by metric name over MCP: Claude, Cursor, Slack bots.

REST API

Programmatic access for custom apps and data pipelines.

Notebooks & apps

Pull governed metrics into Python or your own product.

REST / SDK
Spreadsheets

Governed metrics in Google Sheets for last-mile analysis.

Sheets
"We wanted to define analytics logic centrally and have people reuse it, to get faster speed to insight. With Holistics, we can have correct, reusable measurements and metrics going out there."
Craig Wilson · Staff Data Engineer, ZOE
Programmable

Built like a real programming language, not YAML duct tape.

YAML was built to serialize data, not to model a business, so when logic repeats you copy-paste it or template SQL with Jinja. AML is a real language: functions, types, and inheritance let you write logic once and reuse it everywhere, so nothing drifts.

Config semantic layer · YAML + Jinja
# 50 country tiles = 50 copies of the same block
tiles:
  - type: single_value
    title: "United States"
    metric: orders.revenue
    filter:
      field: users.country
      operator: is
      value: "United States"
  # …49 more, copy-pasted, one per country

# reuse? hardcode the list in Jinja and pray:
{% for c in ["United States", "Germany", …] %}
  - type: single_value
    title: "{{ c }}"
    metric: orders.revenue
    filter: {field: users.country, value: "{{ c }}"}
{% endfor %}
AMLProgrammable semantic layer
Func revenue_kpi(country: String) {
  VizBlock {
    label: country
    viz: SingleValue {
      dataset: ecommerce
      series { field { ref: r(orders.revenue) } }
      filter {
        field: r(users.country)
        operator: 'is'
        value: country
      }
    }
  }
}

Dashboard country_kpis {
  block kpi1: revenue_kpi('United States')
  block kpi2: revenue_kpi('Germany')
  // …one function, 50 tiles
}
"I have KPI widgets that appear in 50 rows of my dashboard. I took one function, parameterized it, and just pass in the variables. The ability to parameterize a widget and reuse it was a game changer."
Mike K. · Director of Enterprise Analytics, Strongmind
Inheritance — extend

Declare the delta, not a copy. Edits to the base flow through; the compiler merges and catches conflicts at build time.

Model active_users = users.extend({
  dimension activated_at {}
})
Functions — Func

Typed, parameterized generators for metrics and partials. Named args, defaults, inferred return types.

Func revenue_for(p: Model) {
  metric order_value {  }
}
Reusable packs — Partial

Bundle a set of metrics once, then snap it onto any dataset. One metric store, reused everywhere.

Dataset us =
  us_market.extend(revenue_pack)
Constants & conditionals

Change a threshold in one place; everything downstream updates. Branch logic with if / else.

const HIGH_VALUE = 25000
Modules & namespaces

Organize logic into modules and reuse the same object across tenants, so multi-client modeling stays clean instead of copying folders.

Typed, with IDE feedback

A static type system validates as you write, with autocomplete, go-to-definition, and compile-time errors. TypeScript for analytics.

"I can simply extend my existing data model by referencing a different schema. Extend gives me a clean way of doing this, so when the schema changes, I don't have to worry about editing in two different places."
Stephen Lee Senior Data Scientist, Aurora
Governed

Your business logic, under version control.

Every definition (models, metrics, dashboards, charts) is plain-text code in Git. Every change goes through a reviewable pull request instead of a silent click in a UI.

Full history & authorship — answer "who changed revenue?" in seconds
PR review on every metric change, with CI validation
Dev / staging / prod environments and one-click rollback
Two-way sync — build with clicks in the GUI, maintain with code
PR #482redefine revenue · net of refunds
metric revenue =
-  sum(orders.amount)
+  sum(order_items.quantity * products.price)
+    | where(orders.status != 'refunded')
Reviewed bydata-platform
CI✓ compiled · 0 type errors
On merge12 dashboards + AI update
AQL revenue, vs. last year
revenue
  | where(orders.status != 'refunded')
  | relative_period(-1 year)
one expression · reuses your model · compiles to native SQL
Query language

Comes with a query language that speaks semantics.

A dedicated analytics query language (AQL) that queries your models, not raw tables, and compiles to native SQL.

Explore the AQL language →
Dependency graph

Change a metric once. Everything downstream follows.

Metrics build on other metrics, by name. Redefine the base and everything derived from it updates automatically, with no copies to fall out of sync.

Reference a metric by name — never copy its formula
Redefine the base and everything downstream recomputes
The compiler tracks the graph and flags breaks at build time
AMLmetric graph
metric revenue =
  sum(order_items.quantity * products.price)
feeds
metric arpu = revenue / count(users.id)
feeds
metric ltv = arpu * lifespan_months
Redefine revenuearpu and ltv recompute. No copies, no drift.
"The semantic layer is Holistics' biggest draw, because I was able to build reusable models, dimensions, and metrics — and then describe them as code."
Sterling Paramore Sr Staff Data Engineer, Mainspring Energy
How you build it

Click it, code it, or let AI build it.

Because your whole BI is plain-text AML, you're never locked into one way of working. Build visually in the GUI, write AML in your IDE, or hand the repo to a coding agent. Every path produces the same governed model.

Clicks, in the GUI

Model by clicking. The visual builder writes AML for you and stays in two-way sync with the code.

Code, in your IDE

Write AML directly, with types, autocomplete, and Git. Software rigor for your business logic.

AI agents

Because it's code, agents develop on it natively: the built-in Copilot, or Claude Code and Cursor in your IDE. They write the AML; you review the PR and validate the SQL.

Agentic development →

Every path produces the same AML, governed in Git and compiled to native SQL.

Open

Sits on your stack. Owned by you.

No proprietary engine, no data leaving your warehouse, no logic you can't read. A programmable layer on top of the stack you already run.

It's your code

Definitions are plain-text AML in your own Git repo — not rows in a vendor database. Leave anytime, with readable logic.

Inspectable SQL

Every query compiles to native SQL you can read. No black-box execution engine, nothing hidden.

Runs on your warehouse

Pushes computation down to your data, so nothing moves and results stay fresh. Snowflake, BigQuery, Postgres, Redshift, Databricks, and more.

Complements dbt

Sits above your transformation layer and reads the models you already build. Not a rip-and-replace.

Fair questions

"Isn't this just YAML in Git?"

Isn't this just YAML in Git?

No. YAML in Git is still YAML — no functions, no types, no real composition; reuse means copy-paste or Jinja string-templating. AML is a typed language where reuse (extend, Func, Partial) is a first-class feature the compiler checks for you.

"We add a new client, copy and paste all the data models, and change the data source. Holistics effectively allows us to be repeatable."
David AldredInsight Director, Retail Insight

In production across hundreds of enterprises.