> ## Documentation Index
> Fetch the complete documentation index at: https://platform.atlan.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> To act on Atlan objects, use the Atlan MCP server at https://api.atlan.com/mcp or the SDK. To install or publish skills from a terminal, use the `aireg` CLI; `aireg --map json` prints its command map. The `atlanai` CLI is deprecated. Run a read-only identity check before any write.
> The docs MCP server at /mcp searches these docs only. It cannot read or change Atlan objects.
> SDK packages: Python `atlanai` (PyPI) and TypeScript `@atlanai/sdk` (npm). Show Python first, then TypeScript.
> These docs cover Atlan AI Registry, Atlan's hosted product. The AI registry specification and aireg CLI are maintained separately. /open-source lists publicly reachable signed installers and explains that the source repository and hosted CLI reference currently require GitHub access. Do not claim the repository or its reference docs are publicly accessible. Use these docs for Atlan-specific setup such as the adapter, sign-in and workspaces.

# Metric definition fields

> Every field in a metric and a saved query definition, with types, required fields, limits, warnings, errors, and a complete example.

<Badge color="orange">Preview</Badge>

This page lists every field Metrics Studio stores for a metric and a saved query. You see these fields in Studio's **JSON** view, in an import file, and in what agents read over MCP.

Atlan checks a definition's shape when you save it: required fields, types, names, and references between fields. It never parses or runs the SQL inside it, and it doesn't check that the number is right.

## Item fields

These fields sit around the definition. An import file row uses them.

| Field | Type | Required | Meaning |
| - | - | - | - |
| `name` | string | Yes | The identifier, for example `average_order_value`. Unique among metrics in the workspace, including archived ones. It can't change after saving. Studio asks for lowercase with underscores. |
| `display_name` | string | No | The name people see, for example "Average order value". An import uses `name` when it's missing. For a saved query, use the question. |
| `description` | string | No | One or two sentences on what the number means. |
| `definition` | object | Yes | The metric definition below, or the saved query definition. |
| `source_uri` | string | No | Where the definition came from, for example a file path in your repository. Shown as **Source location**. Never fetched. |
| `source_version` | string | No | The revision of that source, for example a commit. Shown as **Source revision**. |
| `source_metadata` | object | No | Any other details about the source, kept as given. |

A metric's owner is the person recorded on the item, shown as **Owner** or **Unassigned**. The definition has no owner field.

## Metric definition

Spec version `1.0-draft.4`. Unknown fields are refused at every level. Put experimental data in `extensions`.

| Field | Type | Required | Meaning |
| - | - | - | - |
| `spec_version` | string | Yes | The format version. Must be `1.0-draft.4`. |
| `version` | integer | Yes | The definition version. A whole number of 1 or more. Raise it when the calculation changes; see [Version rules](#version-rules). |
| `dialect` | string | Yes | The warehouse the SQL is written for, for example `snowflake`. Free text. Shown as **Warehouse**. |
| `formula` | string | Yes | The expression or query that computes the number. Stored as written. |
| `aggregation` | object | Yes | How the formula combines rows. See [`aggregation`](#aggregation). |
| `output` | object | Yes | The result column. See [`output`](#output). |
| `source` | object | Yes | The table or SQL the metric reads. See [`source`](#source). |
| `population` | string | No | In words, which rows the number covers. Shown as **Includes**. |
| `exclusions` | array of strings | No | In words, rows the number leaves out. Shown as **Excludes**. |
| `grain` | string | No | The row level the metric works at, for example `order_id`. Shown as **Granularity**. |
| `filters` | array of strings | No | SQL conditions the metric applies. |
| `mandatory_filters` | array of strings | No | SQL conditions marked as mandatory. Can refer to a parameter as `:name`. |
| `depends_on` | array of strings | No | Names of other metrics this one is built from. Each name that matches a live metric in the workspace becomes a `depends_on` link. |
| `replaced_by` | string | No | Name of the metric that replaces this one. Becomes a `replaced_by` link. Doesn't withdraw or archive anything. |
| `joins` | array of objects | No | Other tables the metric joins. See [`joins[]`](#joins). |
| `dimensions` | array of objects | No | Ways to break the number down. See [`dimensions[]`](#dimensions). |
| `examples` | array of objects | No | Questions the metric answers, with their SQL. See [`examples[]`](#examples). |
| `provenance` | object | No | How the definition was observed. See [`provenance`](#provenance). |
| `extensions` | object | No | Any extra data. Kept as given, with a warning, and never interpreted. |

### `aggregation`

| Field | Type | Required | Meaning |
| - | - | - | - |
| `type` | string | Yes | One of `sum`, `count`, `count_distinct`, `min`, `max`, `ratio`, `custom`. Shown as **Aggregation type**. |
| `reaggregation` | string | No | How the number rolls up across groups: `sum`, `min`, `max`, or `none`. Shown as **Rolling up**. |
| `null_handling` | string | No | How missing values count: `ignore`, `zero_fill`, or `propagate`. Shown as **Missing values**. |
| `zero_denominator` | any JSON value | No | What a ratio does when it divides by zero. Shown as **Dividing by zero**. |

A value outside the listed words is kept, with a warning.

### `output`

| Field | Type | Required | Meaning |
| - | - | - | - |
| `alias` | string | Yes | The result column name. Shown as **Output name**. |
| `datatype` | string | Yes | One of `integer`, `decimal`, `string`, `boolean`, `date`, `timestamp`. Shown as **Output data type**. |
| `unit` | string | No | For example `USD` or `orders`. Shown as **Output unit**. |

### `source`

| Field | Type | Required | Meaning |
| - | - | - | - |
| `entity` | string | Yes | What the rows represent, for example `orders`. Shown as **Source entity**. |
| `alias` | string | Yes | The alias the formula uses, for example `o`. Shown as **Source alias**. |
| `table` | string | One of `table` or `sql` | The table to read, for example `analytics.sales.orders`. |
| `sql` | string | One of `table` or `sql` | SQL whose result the metric reads instead of a table. |
| `timeframe_column` | string | No | The column that dates each row. Shown as **Time column**. |
| `parameters` | array of objects | No | Values a caller supplies. Each has `name` (required, unique), `datatype` (required), and `default` (required, any JSON value, can be `null`). |

Give exactly one of `table` and `sql`, and it can't be empty.

### `joins[]`

| Field | Type | Required | Meaning |
| - | - | - | - |
| `name` | string | Yes | Unique among the metric's joins. |
| `to` | string | Yes | The table or entity joined. |
| `sql` | string | Yes | The join condition. |
| `cardinality` | string | Yes | For example `many_to_one`. Free text. When a value ends in `many`, such as `one_to_many`, Studio warns that breaking the number down through the join can count a row more than once. |
| `origin` | string | Yes | Where the join came from. Studio reads `declared` as stated in the definition and `observed` as worked out from past queries. |

### `dimensions[]`

| Field | Type | Required | Meaning |
| - | - | - | - |
| `name` | string | Yes | Unique among the metric's dimensions. |
| `kind` | string | Yes | For example `time` or `categorical`. Free text. |
| `sql` | string | Yes | The expression for the dimension's values. |
| `datatype` | string | Yes | The values' data type. |
| `filter_form` | string | No | How to filter on it, for example `equals`, `in list`, or `range`. |
| `values` | array | No | Observed values, in the order given. |
| `values_truncated` | any JSON value | No | Whether `values` is only part of the list. |
| `value_map` | any JSON value | No | A mapping of values, kept as given. |
| `required_join` | string | No | The name of a join in `joins` this dimension needs. It must exist. |
| `description` | string | No | What the dimension means. |

### `examples[]`

| Field | Type | Required | Meaning |
| - | - | - | - |
| `source` | string | Yes | Who or what supplied the example. |
| `question` | string | No | The question in words. |
| `group_by` | array of strings | No | Dimension names. Each must exist in `dimensions`. |
| `filters` | object | No | Filter values used, by name. |
| `parameters` | object | No | Parameter values used, by name. |
| `sql` | string | Yes | The SQL that answers the question. |
| `result` | object | No | A sample result, as supplied. Atlan doesn't verify it. Has `columns` (array of strings), `row_count` (integer), `hash` (string), `executed_at` (string), and `rows` (array of rows, each an array of strings or `null`). Every row needs as many cells as `columns` has. |

### `provenance`

| Field | Type | Required | Meaning |
| - | - | - | - |
| `source` | string | Yes | Where the observation came from, for example `query_history`. |
| `proposed_by` | string | Yes | Who proposed the definition. |
| `execution_count` | integer | No | How many times the observed SQL ran. |
| `distinct_analysts` | integer | No | How many people ran it. |
| `trust_score` | number | No | A score from your own tooling. It grants no access. |
| `observed_in` | array of strings | No | Where it was seen. Never fetched. |
| `observed_sql` | string | No | The SQL as observed. |
| `derived_from` | array of strings | No | What it was derived from. Never fetched. |

### Version rules

When you save a new revision of a metric, Atlan compares it with the current one:

* If the calculation changed, `version` must be higher. The calculation covers `spec_version`, `dialect`, `formula`, `aggregation`, `output`, `filters`, `mandatory_filters`, `source`, `joins`, the SQL parts of `dimensions` (including `value_map`), `depends_on`, `grain`, `population`, `exclusions`, and `extensions`.
* If only `display_name`, `description`, `examples`, `provenance`, `replaced_by`, or a dimension's `values` or `description` changed, `version` can stay the same.
* `version` can never go down.

Studio raises `version` by 1 each time you edit a metric. The definition version is separate from the revision number and the release version. See [Three different version numbers](/studios/metrics/concepts#three-different-version-numbers).

## Saved query definition

Spec version `1.0-draft.1`. Unknown fields are refused. Only `spec_version` and `sql` are required, but a field you include can't be blank.

| Field | Type | Required | Meaning |
| - | - | - | - |
| `spec_version` | string | Yes | Must be `1.0-draft.1`. |
| `question` | string | No | The question the SQL answers, up to 1,000 characters. The main text agents search on. Shown as **Question**. |
| `paraphrases` | array of strings | No | Other ways to ask it. Up to 16, each up to 1,000 characters, each different from the question and from each other (ignoring case). |
| `sql` | string | Yes | The SQL, up to 64 KiB. Stored as written. Shown as **SQL text**. |
| `dialect` | string | No | The warehouse, up to 64 characters. Free text. Shown as **Warehouse**. |
| `parameters` | array of objects | No | Named values the SQL expects. Each has `name` (required), `data_type`, and `description`. Names are unique, ignoring case. |
| `output_columns` | array of objects | No | Result columns. Each has `name` (required) and `data_type`. Names are unique, ignoring case. |
| `analysis` | object | No | Structure extracted by your own tooling: `tables`, `columns`, `dimensions`, `measures`, `filters` (each `{column, operator, value}`), and `extractor`, for example `sqlglot 26.1`. Atlan checks its shape only and never compares it with the SQL. |
| `usage` | object | No | `execution_count`, `user_count`, and `last_executed_at` (an RFC 3339 timestamp), as you report them. |
| `review_status` | string | No | `unreviewed` (default), `verified`, or `rejected`. Studio shows `verified` as **Ran when saved: Yes** and `unreviewed` as **Not run**. It's your claim, not a check by Atlan. |
| `usage_note` | string | No | Guidance for an agent reusing the query, up to 4,000 characters. Shown as **Usage notes**. |
| `extensions` | object | No | Any extra data, kept as given with a warning. |

Agents can find a saved query by its question, paraphrases, and usage note, and by the table, column, parameter, and output names in it. SQL text and filter values are never searched.

## Fields Atlan adds

Atlan computes these on every save. Anything you send for them is replaced.

| Field | Meaning |
| - | - |
| `definition_sha256` | A fingerprint of the whole definition. Key order and whitespace don't change it. |
| `execution_definition_sha256` | Metrics only. A fingerprint of the calculation fields listed in [Version rules](#version-rules). |
| `sql_sha256` | Saved queries only. A fingerprint of the SQL with whitespace collapsed, used to spot duplicate SQL. |
| `validation_warnings` | Advisory warnings, listed below. They never block a save. |
| `sql_validation` | Always `not_performed`. |

### Warnings

| Warning | Applies to | When |
| - | - | - |
| `pilot_spec` | Both | Always. The format is a preview format, and saving doesn't prove the SQL or the number. |
| `draft_vocabulary` | Metric | `aggregation.type`, `reaggregation`, `null_handling`, or `output.datatype` uses a word outside its list. |
| `ratio_policy` | Metric | `aggregation.type` is `ratio` and `zero_denominator` is missing or `null`. |
| `examples_missing` | Metric | `examples` is empty. |
| `experimental_fields` | Metric | `extensions` has data. |
| `question_missing` | Saved query | No `question`. |
| `dialect_missing` | Saved query | No `dialect`. |
| `analysis_missing` | Saved query | No tables in `analysis`. |
| `extensions` | Saved query | `extensions` has data. |

## Complete example

<CodeGroup>
  ```json Metric: average_order_value theme={null}
  {
    "name": "average_order_value",
    "display_name": "Average order value",
    "description": "Average amount a customer pays per completed order.",
    "source_uri": "metrics/orders/average_order_value.json",
    "source_version": "9f2c1e7",
    "definition": {
      "spec_version": "1.0-draft.4",
      "version": 1,
      "dialect": "snowflake",
      "formula": "SUM(o.order_total) / COUNT(DISTINCT o.order_id)",
      "aggregation": {
        "type": "ratio",
        "reaggregation": "none",
        "null_handling": "ignore",
        "zero_denominator": "return_null"
      },
      "output": {
        "alias": "average_order_value",
        "datatype": "decimal",
        "unit": "USD"
      },
      "population": "Completed orders from the online store",
      "exclusions": ["Test orders placed by staff accounts"],
      "grain": "order_id",
      "filters": ["o.order_total > 0"],
      "mandatory_filters": [
        "o.status = 'completed'",
        "o.ordered_at >= :start_date"
      ],
      "depends_on": ["revenue"],
      "source": {
        "entity": "orders",
        "table": "analytics.sales.orders",
        "alias": "o",
        "timeframe_column": "o.ordered_at",
        "parameters": [
          { "name": "start_date", "datatype": "date", "default": null }
        ]
      },
      "joins": [
        {
          "name": "customers",
          "to": "analytics.sales.customers",
          "sql": "o.customer_id = c.customer_id",
          "cardinality": "many_to_one",
          "origin": "declared"
        }
      ],
      "dimensions": [
        {
          "name": "order_month",
          "kind": "time",
          "sql": "DATE_TRUNC('month', o.ordered_at)",
          "datatype": "date",
          "description": "Month the order was placed"
        },
        {
          "name": "customer_region",
          "kind": "categorical",
          "sql": "c.region",
          "datatype": "string",
          "filter_form": "in list",
          "values": ["APAC", "EMEA", "NA"],
          "required_join": "customers"
        }
      ],
      "examples": [
        {
          "source": "analyst",
          "question": "What was average order value by month this year?",
          "group_by": ["order_month"],
          "parameters": { "start_date": "2026-01-01" },
          "sql": "SELECT DATE_TRUNC('month', o.ordered_at) AS order_month, SUM(o.order_total) / COUNT(DISTINCT o.order_id) AS average_order_value FROM analytics.sales.orders o WHERE o.status = 'completed' AND o.order_total > 0 AND o.ordered_at >= '2026-01-01' GROUP BY 1 ORDER BY 1",
          "result": {
            "columns": ["order_month", "average_order_value"],
            "row_count": 2,
            "hash": "b41d07",
            "executed_at": "2026-10-01T09:00:00Z",
            "rows": [
              ["2026-01-01", "52.10"],
              ["2026-02-01", "54.80"]
            ]
          }
        }
      ],
      "provenance": {
        "source": "query_history",
        "proposed_by": "data-team",
        "execution_count": 412,
        "distinct_analysts": 9
      }
    }
  }
  ```

  ```json Saved query: revenue by month theme={null}
  {
    "name": "revenue_by_month_last_year",
    "display_name": "What was revenue by month last year?",
    "definition": {
      "spec_version": "1.0-draft.1",
      "question": "What was revenue by month last year?",
      "paraphrases": [
        "Monthly revenue for last year",
        "Show last year's revenue per month"
      ],
      "sql": "SELECT DATE_TRUNC('month', ordered_at) AS order_month, SUM(order_total) AS revenue FROM analytics.sales.orders WHERE status = 'completed' AND ordered_at >= :start_date AND ordered_at < :end_date GROUP BY 1 ORDER BY 1",
      "dialect": "snowflake",
      "parameters": [
        { "name": "start_date", "data_type": "date", "description": "First day of last year" },
        { "name": "end_date", "data_type": "date", "description": "First day of this year" }
      ],
      "output_columns": [
        { "name": "order_month", "data_type": "date" },
        { "name": "revenue", "data_type": "decimal" }
      ],
      "analysis": {
        "tables": ["analytics.sales.orders"],
        "columns": ["ordered_at", "order_total", "status"],
        "dimensions": ["order_month"],
        "measures": ["revenue"],
        "filters": [{ "column": "status", "operator": "=", "value": "completed" }],
        "extractor": "sqlglot 26.1"
      },
      "usage": {
        "execution_count": 128,
        "user_count": 7,
        "last_executed_at": "2026-10-15T14:02:00Z"
      },
      "review_status": "verified",
      "usage_note": "Counts completed orders only. For the amount per order, use the average_order_value metric."
    }
  }
  ```
</CodeGroup>

## Limits

| Limit | Value |
| - | - |
| Size of one definition | 1 MiB |
| Any single string or key | 256 KiB |
| Nesting depth | 32 levels |
| Values in one definition | 16,384 |
| Entries in one list or object | 1,024 |
| Characters | No NUL characters, anywhere |
| One bulk save | 32 definitions and 4 MiB of definitions |
| Saved query SQL | 64 KiB |
| Saved query question and each paraphrase | 1,000 characters |
| Paraphrases | 16 |
| Saved query usage note and parameter description | 4,000 characters |
| Saved query names in `parameters`, `output_columns`, and `analysis` | 256 characters each |
| Studio import file | 1 MiB; up to 100 metrics or 1,000 query records |
| Studio release notes and withdrawal reason | 4,000 characters |

## Errors

When a save is refused, the error names the field it's about, for example `Field 'definition.source' is not valid.` It never repeats the values or SQL you sent.

| Status | Meaning | What to do |
| - | - | - |
| `422` | The definition doesn't fit the format. See the table below for each field. A metric whose calculation changed without a higher `version` is also refused here, at `definition.version`. | Fix the named field and save again. |
| `409` | Someone else saved at the same moment. | Reload the current revision, then save again. |
| `409` | The stored revision is in a format this save can't build on. | Retrying won't help. Contact your Atlan administrator. |
| `413` | A bulk save had more than 32 definitions or more than 4 MiB of definitions. | Split it into smaller batches. |
| `403` or `404` | You can't edit this item, or it doesn't exist. | Check the workspace and your role. |
| `503` | Atlan is busy, or couldn't read the current revision in time. | Try again. |

Common `422` fields:

| Field in the error | Why |
| - | - |
| `definition` | An unknown field, a wrong type, or a size limit was exceeded. |
| `definition.spec_version` | Not `1.0-draft.4` for a metric, or not `1.0-draft.1` for a saved query. |
| `definition.version` | Not 1 or more, lower than the current version, or the calculation changed without raising it. |
| `definition.dialect`, `definition.formula`, `definition.aggregation.type`, `definition.output.alias`, `definition.output.datatype`, `definition.source.entity`, `definition.source.alias` | Empty. |
| `definition.source` | Not exactly one non-empty `table` or `sql`. |
| `definition.joins`, `definition.dimensions`, `definition.source.parameters` | A name is empty or used twice. |
| `definition.dimensions.required_join` | Names a join that isn't in `joins`. |
| `definition.examples.group_by` | Names a dimension that isn't in `dimensions`. |
| `definition.examples.result.rows` | A row has a different number of cells than `columns`. |
| `definition.sql` | Saved query SQL is empty or over 64 KiB. |
| `definition.question`, `definition.paraphrases` | Blank, too long, more than 16 paraphrases, or a paraphrase repeats the question or another paraphrase. |
| `definition.usage`, `definition.usage.last_executed_at` | A count is too large, or the timestamp isn't RFC 3339. |

## Related

* [How metrics work](/studios/metrics/concepts): What each part means and how versions differ.
* [Write metrics](/studios/metrics/how-tos/write-metrics): Fill in these fields in Studio.
* [Import metrics and queries](/studios/metrics/how-tos/import-metrics-and-queries): Load many definitions from a file.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.