by Holistics
Try with anfra

Semantic layer · Analytics 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.

AMLecommerce.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)
}

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 warehouseSnowflakeGoogle BigQueryAmazon RedshiftDatabricks
AMLSemantic 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
In-platform

Dashboards & self-serve

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

Learn more
Embed SDK

Embedded analytics

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

Learn more
MCP

AI agents

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

MCP docs
REST

REST API

Programmatic access for custom apps and data pipelines.

API docs
REST / SDK

Notebooks & apps

Pull governed metrics into Python or your own product.

Sheets

Spreadsheets

Governed metrics in Google Sheets for last-mile analysis.

“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 WilsonStaff Data Engineer, ZOE

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

YAMLdashboard.yml
# 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 %}

Programmable semantic layer · AML

AMLcountry_kpis.page.aml
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

Inheritanceextend

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 {}
})

FunctionsFunc

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

Func revenue_for(p: Model) {
  metric order_value { … }
}

Reusable packsPartial

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 LeeSenior Data Scientist, Aurora

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 refundsApproved
AQLmodels/metrics.aml+2 −1
metric revenue =
-  sum(orders.amount)
+  sum(order_items.quantity * products.price)
+    | where(orders.status != 'refunded')
Reviewed by
data-platform
CI
compiled · 0 type errors
On merge
12 dashboards + AI update

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.

AQLrevenue, vs. last year
revenue
  | where(orders.status != 'refunded')
  | relative_period(-1 year)
  • one expression · reuses your model · compiles to native SQL

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)
metric arpu = revenue / count(users.id)
metric ltv = arpu * lifespan_months

Redefine revenue, and arpu 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 ParamoreSr Staff Data Engineer, Mainspring Energy

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

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.

“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

Go deeper: AML vs YAML, the reusability guide, and the AQL query language