diff --git a/docs/reference/toc.yml b/docs/reference/toc.yml
index c04593e902cb1..08d37bd5378c5 100644
--- a/docs/reference/toc.yml
+++ b/docs/reference/toc.yml
@@ -1,6 +1,7 @@
project: 'Kibana reference'
toc:
- file: index.md
+ - toc: visualizations-api
- file: kibana-accessibility-statement.md
- file: configuration-reference.md
children:
diff --git a/docs/reference/visualizations-api/_tables/datatable-density.csv b/docs/reference/visualizations-api/_tables/datatable-density.csv
new file mode 100644
index 0000000000000..07d5c84f79185
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/datatable-density.csv
@@ -0,0 +1,5 @@
+Parameter,Description
+`height`
_object_,
+`height.header`
_object_,"Maximum number of lines to use before header is truncated. Types: `""auto""`, `""custom""`."
+`height.value`
_object_,"Number of lines to display per table body cell. Types: `""auto""`, `""custom""`."
+`mode`
_string_,"Density mode. Possible values: `""compact""`, `""default""`, `""expanded""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/datatable.csv b/docs/reference/visualizations-api/_tables/datatable.csv
new file mode 100644
index 0000000000000..bb9fcde20c512
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/datatable.csv
@@ -0,0 +1,21 @@
+Parameter,Description
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`, `esqlDataSource`."
+[`density`](#density)
_object_,Density configuration for the datatable.
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`metrics`
_array_
**required**,"Array of metrics to display as columns in the datatable. Items: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `differencesOperation`, `movingAverageOperation`, `cumulativeSumOperation`, `counterRateOperation`, `formulaOperation`."
+`paging`
_number_,"Enables pagination and sets the number of rows to display per page. Possible values: `10`, `20`, `30`, `50`, `100`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`row_numbers`
_object_,Configuration for row numbers.
+`rows`
_array_,"Array of operations to split the datatable rows by. Items: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`sort_by`
_object_,"Sorting configuration. Only one column can be sorted at a time. Use ""column_type"" to specify the column type."
+`split_metrics_by`
_array_,"Array of operations to split the metric columns by. Items: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""data_table""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/gauge.csv b/docs/reference/visualizations-api/_tables/gauge.csv
new file mode 100644
index 0000000000000..94b76dcefc25f
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/gauge.csv
@@ -0,0 +1,16 @@
+Parameter,Description
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`, `esqlDataSource`."
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`metric`
_object_
**required**,"One of: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `formulaOperation`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`shape`
_object_,"One of: `gaugeShapeBullet`, `gaugeShapeCircular`."
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""gauge""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/heatmap-axes.csv b/docs/reference/visualizations-api/_tables/heatmap-axes.csv
new file mode 100644
index 0000000000000..f35bb2c469eb1
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/heatmap-axes.csv
@@ -0,0 +1,17 @@
+Parameter,Description
+`x`
_object_,X axis configuration.
+`x.labels`
_object_,
+`x.labels.orientation`
_object_
**required**,"Orientation of the tagcloud. Possible values: `""horizontal""`, `""vertical""`, `""angled""`."
+`x.labels.visible`
_boolean_,Show axis labels.
+`x.scale`
_string_
**required**,"X-axis scale type. Use 'temporal' for timestamp/date fields (e.g., @timestamp, DATE_TRUNC results). Use 'ordinal' for categorical/text fields. Use 'linear' for numeric fields. Possible values: `""ordinal""`, `""temporal""`, `""linear""`."
+`x.sort`
_string_,"Axis sort order; omit or use undefined for no sorting. Possible values: `""asc""`, `""desc""`."
+`x.title`
_object_,
+`x.title.text`
_string_,Axis title text.
+`x.title.visible`
_boolean_,Show the title.
+`y`
_object_,Y axis configuration.
+`y.labels`
_object_,
+`y.labels.visible`
_boolean_,Show axis labels.
+`y.sort`
_string_,"Axis sort order; omit or use undefined for no sorting. Possible values: `""asc""`, `""desc""`."
+`y.title`
_object_,
+`y.title.text`
_string_,Axis title text.
+`y.title.visible`
_boolean_,Show the title.
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/heatmap-cells.csv b/docs/reference/visualizations-api/_tables/heatmap-cells.csv
new file mode 100644
index 0000000000000..460f0a97a52c7
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/heatmap-cells.csv
@@ -0,0 +1,3 @@
+Parameter,Description
+`labels`
_object_,
+`labels.visible`
_boolean_,Show cell labels.
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/heatmap.csv b/docs/reference/visualizations-api/_tables/heatmap.csv
new file mode 100644
index 0000000000000..4d93f957e73ff
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/heatmap.csv
@@ -0,0 +1,20 @@
+Parameter,Description
+[`axes`](#axes)
_object_,Axis configuration for X and Y axes.
+[`cells`](#cells)
_object_,Cells configuration.
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`, `esqlDataSource`."
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`legend`
_object_,Legend configuration.
+`metric`
_object_
**required**,"One of: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `differencesOperation`, `movingAverageOperation`, `cumulativeSumOperation`, `counterRateOperation`, `formulaOperation`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""heatmap""`."
+`x`
_object_
**required**,"One of: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`y`
_object_,"One of: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/legacy-metric.csv b/docs/reference/visualizations-api/_tables/legacy-metric.csv
new file mode 100644
index 0000000000000..24a43ddf3baf0
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/legacy-metric.csv
@@ -0,0 +1,15 @@
+Parameter,Description
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`."
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`metric`
_object_
**required**,"One of: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `formulaOperation`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""legacy_metric""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/metric-styling.csv b/docs/reference/visualizations-api/_tables/metric-styling.csv
new file mode 100644
index 0000000000000..384e30fdd20fe
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/metric-styling.csv
@@ -0,0 +1,17 @@
+Parameter,Description
+`primary`
_object_,
+`primary.icon`
_object_
**required**,Icon configuration for the primary metric.
+`primary.icon.alignment`
_string_,"Icon alignment. Possible values: `""left""`, `""right""`."
+`primary.icon.name`
_string_
**required**,"Icon name. Possible values: `""alert""`, `""asterisk""`, `""bell""`, `""bolt""`, `""bug""`, `""compute""`, `""editor_comment""`, `""flag""`, `""globe""`, `""heart""`, `""map_marker""`, `""pin""`, `""sort_down""`, `""sort_up""`, `""star_empty""`, `""tag""`, `""temperature""`."
+`primary.labels`
_object_,Labels (title and subtitle) configuration.
+`primary.labels.alignment`
_string_,"Horizontal alignment for the title and subtitle text (left, center or right). Possible values: `""left""`, `""center""`, `""right""`."
+`primary.position`
_string_,"Position of the primary metric value (top, middle, or bottom). Possible values: `""top""`, `""middle""`, `""bottom""`."
+`primary.value`
_object_,Primary metric value configuration.
+`primary.value.alignment`
_string_,"Alignment for the primary metric value (left, center or right). Possible values: `""left""`, `""center""`, `""right""`."
+`primary.value.sizing`
_string_,"Controls how the primary value text is sized within the panel. 'auto' selects a font size from predefined breakpoints based on panel height, then shrinks if the text overflows horizontally. 'fill' scales the text to be as large as possible, filling all available space. Possible values: `""auto""`, `""fill""`."
+`secondary`
_object_,
+`secondary.label`
_object_,
+`secondary.label.placement`
_string_,"Label placement relative to the secondary metric value (before or after). Possible values: `""before""`, `""after""`."
+`secondary.label.visible`
_boolean_,Whether to display the label.
+`secondary.value`
_object_,Secondary metric value configuration.
+`secondary.value.alignment`
_string_,"Alignment for secondary values (left, center or right). Possible values: `""left""`, `""center""`, `""right""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/metric.csv b/docs/reference/visualizations-api/_tables/metric.csv
new file mode 100644
index 0000000000000..bdc82246d1bf5
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/metric.csv
@@ -0,0 +1,17 @@
+Parameter,Description
+`breakdown_by`
_object_,"One of: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`, `esqlDataSource`."
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`metrics`
_array_
**required**,"Items: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `differencesOperation`, `movingAverageOperation`, `cumulativeSumOperation`, `counterRateOperation`, `formulaOperation`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+[`styling`](#styling)
_object_,Visual styling options for the chart.
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""metric""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/mosaic-legend.csv b/docs/reference/visualizations-api/_tables/mosaic-legend.csv
new file mode 100644
index 0000000000000..8c8e181b28d6f
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/mosaic-legend.csv
@@ -0,0 +1,5 @@
+Parameter,Description
+`nested`
_boolean_,Show nested legend with hierarchical breakdown levels.
+`size`
_object_,"Legend size. Possible values: `""auto""`, `""s""`, `""m""`, `""l""`, `""xl""`."
+`truncate_after_lines`
_number_,Maximum lines before truncating legend items (1-10).
+`visibility`
_string_,"Legend visibility. Possible values: `""auto""`, `""visible""`, `""hidden""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/mosaic.csv b/docs/reference/visualizations-api/_tables/mosaic.csv
new file mode 100644
index 0000000000000..0659af7c4aeab
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/mosaic.csv
@@ -0,0 +1,19 @@
+Parameter,Description
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`, `esqlDataSource`."
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`group_breakdown_by`
_array_,"Array of group breakdown dimensions (minimum 1). Items: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`group_by`
_array_,"Array of breakdown dimensions (minimum 1). Items: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+[`legend`](#legend)
_object_,Legend configuration for mosaic chart appearance and behavior.
+`metric`
_object_
**required**,"One of: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `differencesOperation`, `movingAverageOperation`, `cumulativeSumOperation`, `counterRateOperation`, `formulaOperation`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""mosaic""`."
+`values`
_object_,Configure the visibility and the format of the values rendered on each chart partition section.
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/pie-legend.csv b/docs/reference/visualizations-api/_tables/pie-legend.csv
new file mode 100644
index 0000000000000..8c8e181b28d6f
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/pie-legend.csv
@@ -0,0 +1,5 @@
+Parameter,Description
+`nested`
_boolean_,Show nested legend with hierarchical breakdown levels.
+`size`
_object_,"Legend size. Possible values: `""auto""`, `""s""`, `""m""`, `""l""`, `""xl""`."
+`truncate_after_lines`
_number_,Maximum lines before truncating legend items (1-10).
+`visibility`
_string_,"Legend visibility. Possible values: `""auto""`, `""visible""`, `""hidden""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/pie.csv b/docs/reference/visualizations-api/_tables/pie.csv
new file mode 100644
index 0000000000000..62670c8ee55d4
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/pie.csv
@@ -0,0 +1,20 @@
+Parameter,Description
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`, `esqlDataSource`."
+`description`
_string_,
+`donut_hole`
_string_,"Donut hole size: none (pie), or s/m/l. Possible values: `""none""`, `""s""`, `""m""`, `""l""`."
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`group_by`
_array_,"Array of breakdown dimensions (minimum 1). Items: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`labels`
_object_,Label configuration for pie chart slice labels inside or outside the pie.
+[`legend`](#legend)
_object_,Legend configuration for pie chart.
+`metrics`
_array_
**required**,"Array of metric configurations (minimum 1). Items: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `differencesOperation`, `movingAverageOperation`, `cumulativeSumOperation`, `counterRateOperation`, `formulaOperation`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""pie""`."
+`values`
_object_,Configure the visibility and the format of the values rendered on each chart partition section.
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/region-map.csv b/docs/reference/visualizations-api/_tables/region-map.csv
new file mode 100644
index 0000000000000..03f945dbd502a
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/region-map.csv
@@ -0,0 +1,16 @@
+Parameter,Description
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`, `esqlDataSource`."
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`metric`
_object_
**required**,"One of: `fieldMetricOperations`, `formulaOperation`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`region`
_object_
**required**,"One of: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""region_map""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/shared-drilldowns.csv b/docs/reference/visualizations-api/_tables/shared-drilldowns.csv
new file mode 100644
index 0000000000000..2529edc9cb2a7
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/shared-drilldowns.csv
@@ -0,0 +1,10 @@
+Parameter,Description
+`dashboard_id`
_string_,
+`label`
_string_,
+`open_in_new_tab`
_boolean_,"When enabled, the dashboard opens in a new browser tab. Default: `false`."
+`trigger`
_string_,"Possible values: `""on_apply_filter""`, `""on_click_row""`, `""on_click_value""`, `""on_open_panel_menu""`, `""on_select_range""`."
+`type`
_string_,"Possible values: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`use_filters`
_boolean_,"When enabled, filters are passed to the opening dashboard. Default: `true`."
+`use_time_range`
_boolean_,"When enabled, time range is passed to the opening dashboard. Default: `true`."
+`encode_url`
_boolean_,"When true, URL is escaped using percent encoding. Default: `true`."
+`url`
_string_,Templated Url. Variables documented at https://www.elastic.co/docs/explore-analyze/dashboards/drilldowns#url-template-variable.
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/tagcloud.csv b/docs/reference/visualizations-api/_tables/tagcloud.csv
new file mode 100644
index 0000000000000..f44220e5595d1
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/tagcloud.csv
@@ -0,0 +1,19 @@
+Parameter,Description
+`caption`
_object_,Caption configuration representing the metric and the tag_by operations labels.
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`, `esqlDataSource`."
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`font_size`
_object_,Minimum and maximum font size for the tags.
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`metric`
_object_
**required**,"One of: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `differencesOperation`, `movingAverageOperation`, `cumulativeSumOperation`, `counterRateOperation`, `formulaOperation`."
+`orientation`
_object_,"Orientation of the tagcloud. Possible values: `""horizontal""`, `""vertical""`, `""angled""`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`tag_by`
_object_
**required**,"One of: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""tag_cloud""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/treemap-legend.csv b/docs/reference/visualizations-api/_tables/treemap-legend.csv
new file mode 100644
index 0000000000000..8c8e181b28d6f
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/treemap-legend.csv
@@ -0,0 +1,5 @@
+Parameter,Description
+`nested`
_boolean_,Show nested legend with hierarchical breakdown levels.
+`size`
_object_,"Legend size. Possible values: `""auto""`, `""s""`, `""m""`, `""l""`, `""xl""`."
+`truncate_after_lines`
_number_,Maximum lines before truncating legend items (1-10).
+`visibility`
_string_,"Legend visibility. Possible values: `""auto""`, `""visible""`, `""hidden""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/treemap.csv b/docs/reference/visualizations-api/_tables/treemap.csv
new file mode 100644
index 0000000000000..a143258e6aff1
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/treemap.csv
@@ -0,0 +1,19 @@
+Parameter,Description
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`, `esqlDataSource`."
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`group_by`
_array_,"Array of breakdown dimensions (minimum 1). Items: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`labels`
_object_,Labels configuration.
+[`legend`](#legend)
_object_,Configuration for the treemap chart legend appearance and behavior.
+`metrics`
_array_
**required**,"Array of metric configurations (minimum 1). Items: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `differencesOperation`, `movingAverageOperation`, `cumulativeSumOperation`, `counterRateOperation`, `formulaOperation`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""treemap""`."
+`values`
_object_,Configure the visibility and the format of the values rendered on each chart partition section.
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/waffle-legend.csv b/docs/reference/visualizations-api/_tables/waffle-legend.csv
new file mode 100644
index 0000000000000..b228ff9c296e4
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/waffle-legend.csv
@@ -0,0 +1,5 @@
+Parameter,Description
+`size`
_object_,"Legend size. Possible values: `""auto""`, `""s""`, `""m""`, `""l""`, `""xl""`."
+`truncate_after_lines`
_number_,Maximum lines before truncating legend items (1-10).
+`values`
_array_,
+`visibility`
_string_,"Legend visibility. Possible values: `""auto""`, `""visible""`, `""hidden""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/waffle.csv b/docs/reference/visualizations-api/_tables/waffle.csv
new file mode 100644
index 0000000000000..be1a27fc89a67
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/waffle.csv
@@ -0,0 +1,18 @@
+Parameter,Description
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`, `esqlDataSource`."
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`group_by`
_array_,"Array of breakdown dimensions (minimum 1). Items: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+[`legend`](#legend)
_object_,Legend configuration for waffle chart.
+`metrics`
_array_
**required**,"Array of metric configurations (minimum 1). Items: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `differencesOperation`, `movingAverageOperation`, `cumulativeSumOperation`, `counterRateOperation`, `formulaOperation`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""waffle""`."
+`values`
_object_,Configure the visibility and the format of the values rendered on each chart partition section.
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/xy-axis.csv b/docs/reference/visualizations-api/_tables/xy-axis.csv
new file mode 100644
index 0000000000000..d35b4d2ed240e
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/xy-axis.csv
@@ -0,0 +1,39 @@
+Parameter,Description
+`secondary_y`
_object_,"Y-axis configuration with optional anchor, scale, and bounds. The anchor controls which side of the chart the axis renders on."
+`secondary_y.anchor`
_string_,"Position of the Y-axis relative to the chart orientation: start places it on the leading side (left in vertical charts), end on the trailing side (right in vertical charts). Possible values: `""start""`, `""end""`."
+`secondary_y.domain`
_object_
**required**,"Y-axis domain configuration. One of: `vis_api_domain_full`, `vis_api_domain_fit`, `vis_api_domain_custom`."
+`secondary_y.grid`
_object_,Axis grid lines configuration.
+`secondary_y.grid.visible`
_boolean_
**required**,Show grid lines for this axis.
+`secondary_y.labels`
_object_,Label configuration.
+`secondary_y.labels.orientation`
_object_
**required**,"Orientation of the tagcloud. Possible values: `""horizontal""`, `""vertical""`, `""angled""`."
+`secondary_y.scale`
_string_,"Y-axis scale type for data transformation. Possible values: `""linear""`, `""log""`, `""sqrt""`."
+`secondary_y.ticks`
_object_,Axis tick marks configuration.
+`secondary_y.ticks.visible`
_boolean_
**required**,Show tick marks on the axis.
+`secondary_y.title`
_object_,Axis title configuration.
+`secondary_y.title.text`
_string_,Axis title text.
+`secondary_y.title.visible`
_boolean_,Show the title.
+`x`
_object_,X-axis configuration.
+`x.domain`
_object_,"X-axis domain configuration. One of: `vis_api_domain_fit`, `vis_api_domain_custom`."
+`x.grid`
_object_,Axis grid lines configuration.
+`x.grid.visible`
_boolean_
**required**,Show grid lines for this axis.
+`x.labels`
_object_,Label configuration.
+`x.labels.orientation`
_object_
**required**,"Orientation of the tagcloud. Possible values: `""horizontal""`, `""vertical""`, `""angled""`."
+`x.scale`
_string_,"X-axis scale type. Use 'temporal' for timestamp/date fields (e.g., @timestamp, DATE_TRUNC results). Use 'ordinal' for categorical/text fields. Use 'linear' for numeric fields. Possible values: `""ordinal""`, `""temporal""`, `""linear""`."
+`x.ticks`
_object_,Axis tick marks configuration.
+`x.ticks.visible`
_boolean_
**required**,Show tick marks on the axis.
+`x.title`
_object_,Axis title configuration.
+`x.title.text`
_string_,Axis title text.
+`x.title.visible`
_boolean_,Show the title.
+`y`
_object_,"Y-axis configuration with optional anchor, scale, and bounds. The anchor controls which side of the chart the axis renders on."
+`y.anchor`
_string_,"Position of the Y-axis relative to the chart orientation: start places it on the leading side (left in vertical charts), end on the trailing side (right in vertical charts). Possible values: `""start""`, `""end""`."
+`y.domain`
_object_
**required**,"Y-axis domain configuration. One of: `vis_api_domain_full`, `vis_api_domain_fit`, `vis_api_domain_custom`."
+`y.grid`
_object_,Axis grid lines configuration.
+`y.grid.visible`
_boolean_
**required**,Show grid lines for this axis.
+`y.labels`
_object_,Label configuration.
+`y.labels.orientation`
_object_
**required**,"Orientation of the tagcloud. Possible values: `""horizontal""`, `""vertical""`, `""angled""`."
+`y.scale`
_string_,"Y-axis scale type for data transformation. Possible values: `""linear""`, `""log""`, `""sqrt""`."
+`y.ticks`
_object_,Axis tick marks configuration.
+`y.ticks.visible`
_boolean_
**required**,Show tick marks on the axis.
+`y.title`
_object_,Axis title configuration.
+`y.title.text`
_string_,Axis title text.
+`y.title.visible`
_boolean_,Show the title.
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/xy-layer-annotation.csv b/docs/reference/visualizations-api/_tables/xy-layer-annotation.csv
new file mode 100644
index 0000000000000..488116bdbe277
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/xy-layer-annotation.csv
@@ -0,0 +1,5 @@
+Parameter,Description
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`."
+`events`
_array_
**required**,"Array of annotation configurations. Items: `xyAnnotationQuery`, `xyAnnotationManualEvent`, `xyAnnotationManualRange`."
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`type`
_string_
**required**,"Possible values: `""annotations""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/xy-layer-data.csv b/docs/reference/visualizations-api/_tables/xy-layer-data.csv
new file mode 100644
index 0000000000000..124ebdc206335
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/xy-layer-data.csv
@@ -0,0 +1,25 @@
+Parameter,Description
+`breakdown_by`
_object_,"One of: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`."
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`type`
_string_
**required**,"Chart type for the data layer. Possible values: `""area""`, `""area_percentage""`, `""area_stacked""`, `""bar""`, `""bar_horizontal""`, `""bar_horizontal_stacked""`, `""bar_horizontal_percentage""`, `""bar_percentage""`, `""bar_stacked""`, `""line""`."
+`x`
_object_,"One of: `dateHistogramOperation`, `termsOperation`, `histogramOperation`, `rangesOperation`, `filtersOperation`."
+`y`
_array_
**required**,"Array of metrics to display on Y-axis. Items: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `differencesOperation`, `movingAverageOperation`, `cumulativeSumOperation`, `counterRateOperation`, `formulaOperation`."
+`breakdown_by.collapse_by`
_object_
**required**,"Collapse by function description. Possible values: `""avg""`, `""sum""`, `""max""`, `""min""`."
+`breakdown_by.color`
_object_
**required**,"One of: `categoricalColorMapping`, `gradientColorMapping`."
+`breakdown_by.column`
_string_
**required**,Column to use.
+`breakdown_by.format`
_object_
**required**,"One of: `numericFormat`, `byteFormat`, `durationFormat`, `customFormat`."
+`breakdown_by.label`
_string_,Label for the operation.
+`data_source.query`
_string_,"An ES|QL query that drives the data source. The query must produce a tabular result set; column names are used as field references. Example: ""FROM logs-* | STATS count = COUNT(*) BY host.name""."
+`data_source.type`
_string_
**required**,"Possible values: `""esql""`."
+`x.column`
_string_
**required**,Column to use.
+`x.format`
_object_
**required**,"One of: `numericFormat`, `byteFormat`, `durationFormat`, `customFormat`."
+`x.label`
_string_,Label for the operation.
+`y[].axis_id`
_string_,"ID of the axis definition this Y metric is bound to; links the metric to the matching axis configuration in axis.y or axis.secondary_y. If omitted, the metric is bound to the primary Y axis. Possible values: `""y""`, `""secondary_y""`."
+`y[].color`
_object_
**required**,
+`y[].color.color`
_string_
**required**,The static color to be used for all values.
+`y[].color.type`
_string_
**required**,"Possible values: `""static""`."
+`y[].column`
_string_
**required**,Column to use.
+`y[].format`
_object_
**required**,"One of: `numericFormat`, `byteFormat`, `durationFormat`, `customFormat`."
+`y[].label`
_string_,Label for the operation.
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/xy-layer-reference-line.csv b/docs/reference/visualizations-api/_tables/xy-layer-reference-line.csv
new file mode 100644
index 0000000000000..b36f89b561803
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/xy-layer-reference-line.csv
@@ -0,0 +1,6 @@
+Parameter,Description
+`data_source`
_object_
**required**,"One of: `kbn-data-view-reference-schema`, `kbn-data-view-spec-schema`."
+`ignore_global_filters`
_boolean_,"If true, ignore global filters when fetching data for this layer. Default is false."
+`sampling`
_number_,Sampling factor between 0 (no sampling) and 1 (full sampling). Default is 1.
+`thresholds`
_array_
**required**,"Array of reference line thresholds. Items: `countMetricOperation`, `uniqueCountMetricOperation`, `minMaxAvgMedianStdDevMetricOperation`, `sumMetricOperation`, `lastValueOperation`, `percentileOperation`, `percentileRanksOperation`, `staticOperationDefinition`, `formulaOperation`."
+`type`
_string_
**required**,"Possible values: `""reference_lines""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/xy-legend.csv b/docs/reference/visualizations-api/_tables/xy-legend.csv
new file mode 100644
index 0000000000000..84876084ea41d
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/xy-legend.csv
@@ -0,0 +1,12 @@
+Parameter,Description
+`layout`
_object_,"Types: `""grid""`, `""list""`."
+`placement`
_string_,"Possible values: `""outside""`."
+`position`
_string_,"Possible values: `""top""`, `""bottom""`."
+`statistics`
_array_,Statistics to display in legend.
+`visibility`
_string_,"Legend visibility. Possible values: `""auto""`, `""visible""`, `""hidden""`."
+`layout.truncate`
_object_,
+`layout.truncate.enabled`
_boolean_,Enable truncation of legend items.
+`layout.truncate.max_lines`
_number_,Maximum lines before truncating legend items (1-10).
+`layout.type`
_string_
**required**,"Possible values: `""grid""`."
+`size`
_object_,"Legend size. Possible values: `""auto""`, `""s""`, `""m""`, `""l""`, `""xl""`. Outside vertical legend only."
+`columns`
_number_,Number of legend columns. Inside legend only.
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/xy-styling.csv b/docs/reference/visualizations-api/_tables/xy-styling.csv
new file mode 100644
index 0000000000000..fc0167fc0793a
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/xy-styling.csv
@@ -0,0 +1,19 @@
+Parameter,Description
+`areas`
_object_,Area-specific rendering settings.
+`areas.fill_opacity`
_number_,"Area fill opacity (0-1 typical, max 2 for legacy)."
+`bars`
_object_,Bar-specific rendering settings.
+`bars.data_labels`
_object_,Data label configuration for bar series.
+`bars.data_labels.visible`
_boolean_,Display value labels on bar data points. Default: `false`.
+`bars.minimum_height`
_number_,Minimum bar height in pixels.
+`fitting`
_object_,Missing data interpolation configuration for line and area series.
+`fitting.emphasize`
_boolean_,Visually distinguish fitted segments with a dashed line style and reduced area opacity.
+`fitting.extend`
_string_,"How to render line and area edges when data does not cover the full X domain. Possible values: `""none""`, `""zero""`, `""nearest""`."
+`fitting.type`
_string_
**required**,"Fitting function type for missing data. Possible values: `""none""`, `""zero""`, `""linear""`, `""carry""`, `""lookahead""`, `""average""`, `""nearest""`."
+`interpolation`
_string_,"Curve interpolation method for line and area series. Possible values: `""linear""`, `""smooth""`, `""stepped""`."
+`overlays`
_object_,Visual overlays drawn on top of the chart canvas.
+`overlays.current_time_marker`
_object_,Current time marker configuration.
+`overlays.current_time_marker.visible`
_boolean_,Show current time marker line. Default: `false`.
+`overlays.partial_buckets`
_object_,Partial (incomplete) bucket indicator configuration.
+`overlays.partial_buckets.visible`
_boolean_,Show partial bucket indicators at time range edges. Default: `false`.
+`points`
_object_,Data point marker settings for line and area series.
+`points.visibility`
_string_,"Data point marker visibility on line and area series. Possible values: `""auto""`, `""visible""`, `""hidden""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/_tables/xy.csv b/docs/reference/visualizations-api/_tables/xy.csv
new file mode 100644
index 0000000000000..d5d70e50c4933
--- /dev/null
+++ b/docs/reference/visualizations-api/_tables/xy.csv
@@ -0,0 +1,15 @@
+Parameter,Description
+[`axis`](#axis)
_object_,"Axis configuration for X, primary Y, and secondary Y axes. The primary Y axis defaults to the start (leading) side, and the secondary Y axis defaults to the end (trailing) side."
+`description`
_string_,
+[`drilldowns`](#drilldowns)
_array_,"Types: `""dashboard_drilldown""`, `""discover_drilldown""`, `""url_drilldown""`."
+`filters`
_object_,"Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw Elasticsearch DSL filters."
+`hide_border`
_boolean_,
+`hide_title`
_boolean_,
+[`layers`](#layers)
_array_
**required**,"Chart layers. Items: `xyLayerNoESQL`, `xyReferenceLineLayerNoESQL`, `xyAnnotationLayerNoESQL`, `xyAnnotationByRefLayer`."
+[`legend`](#legend)
_object_,"Legend configuration for XY chart. One of: `xyLegendOutsideHorizontal`, `xyLegendOutsideVertical`, `xyLegendInside`."
+`query`
_object_,Not used with ES|QL data sources.
+`references`
_array_,Items: `kbn-content-management-utils-referenceSchema`.
+[`styling`](#styling)
_object_,Visual styling options for the chart.
+`time_range`
_object_,
+`title`
_string_,
+`type`
_string_
**required**,"Possible values: `""xy""`."
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/datatable.md b/docs/reference/visualizations-api/datatable.md
new file mode 100644
index 0000000000000..bcd60649cfc8d
--- /dev/null
+++ b/docs/reference/visualizations-api/datatable.md
@@ -0,0 +1,23 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Data table
+---
+
+# Data table chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/datatable.csv
+:::
+
+## Density
+
+:::{csv-include} _tables/datatable-density.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/gauge.md b/docs/reference/visualizations-api/gauge.md
new file mode 100644
index 0000000000000..95c10014031c0
--- /dev/null
+++ b/docs/reference/visualizations-api/gauge.md
@@ -0,0 +1,18 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Gauge
+---
+
+# Gauge chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/gauge.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/heatmap.md b/docs/reference/visualizations-api/heatmap.md
new file mode 100644
index 0000000000000..2b69fb5483b30
--- /dev/null
+++ b/docs/reference/visualizations-api/heatmap.md
@@ -0,0 +1,28 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Heatmap
+---
+
+# Heatmap chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/heatmap.csv
+:::
+
+## Axes
+
+:::{csv-include} _tables/heatmap-axes.csv
+:::
+
+## Cells
+
+:::{csv-include} _tables/heatmap-cells.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/index.md b/docs/reference/visualizations-api/index.md
new file mode 100644
index 0000000000000..a6b93a1819fc6
--- /dev/null
+++ b/docs/reference/visualizations-api/index.md
@@ -0,0 +1,23 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Visualizations API reference
+---
+
+# Visualizations API schema reference
+
+Field-level schema reference for each chart type supported by the Visualizations API.
+
+- [Metric](metric.md)
+- [XY (line, area, bar)](xy.md)
+- [Gauge](gauge.md)
+- [Heatmap](heatmap.md)
+- [Data table](datatable.md)
+- [Pie / donut](pie.md)
+- [Mosaic](mosaic.md)
+- [Treemap](treemap.md)
+- [Waffle](waffle.md)
+- [Tag cloud](tagcloud.md)
+- [Region map](region-map.md)
+- [Legacy metric](legacy-metric.md)
diff --git a/docs/reference/visualizations-api/legacy-metric.md b/docs/reference/visualizations-api/legacy-metric.md
new file mode 100644
index 0000000000000..58b1392f1c322
--- /dev/null
+++ b/docs/reference/visualizations-api/legacy-metric.md
@@ -0,0 +1,18 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Legacy metric
+---
+
+# Legacy metric chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/legacy-metric.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/metric.md b/docs/reference/visualizations-api/metric.md
new file mode 100644
index 0000000000000..bfd283a6b176c
--- /dev/null
+++ b/docs/reference/visualizations-api/metric.md
@@ -0,0 +1,23 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Metric
+---
+
+# Metric chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/metric.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
+
+## Styling
+
+:::{csv-include} _tables/metric-styling.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/mosaic.md b/docs/reference/visualizations-api/mosaic.md
new file mode 100644
index 0000000000000..b9fbf8fdc59a8
--- /dev/null
+++ b/docs/reference/visualizations-api/mosaic.md
@@ -0,0 +1,23 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Mosaic
+---
+
+# Mosaic chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/mosaic.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
+
+## Legend
+
+:::{csv-include} _tables/mosaic-legend.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/pie.md b/docs/reference/visualizations-api/pie.md
new file mode 100644
index 0000000000000..4b0ffdfa2b6cb
--- /dev/null
+++ b/docs/reference/visualizations-api/pie.md
@@ -0,0 +1,23 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Pie / donut
+---
+
+# Pie / donut chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/pie.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
+
+## Legend
+
+:::{csv-include} _tables/pie-legend.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/region-map.md b/docs/reference/visualizations-api/region-map.md
new file mode 100644
index 0000000000000..6b85a1454ca9c
--- /dev/null
+++ b/docs/reference/visualizations-api/region-map.md
@@ -0,0 +1,18 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Region map
+---
+
+# Region map chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/region-map.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/tagcloud.md b/docs/reference/visualizations-api/tagcloud.md
new file mode 100644
index 0000000000000..a6afdfdc68baf
--- /dev/null
+++ b/docs/reference/visualizations-api/tagcloud.md
@@ -0,0 +1,18 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Tag cloud
+---
+
+# Tag cloud chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/tagcloud.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/toc.yml b/docs/reference/visualizations-api/toc.yml
new file mode 100644
index 0000000000000..e7faa3dc8ad10
--- /dev/null
+++ b/docs/reference/visualizations-api/toc.yml
@@ -0,0 +1,15 @@
+project: 'Visualizations API reference'
+toc:
+ - file: index.md
+ - file: metric.md
+ - file: xy.md
+ - file: gauge.md
+ - file: heatmap.md
+ - file: datatable.md
+ - file: pie.md
+ - file: mosaic.md
+ - file: treemap.md
+ - file: waffle.md
+ - file: tagcloud.md
+ - file: region-map.md
+ - file: legacy-metric.md
diff --git a/docs/reference/visualizations-api/treemap.md b/docs/reference/visualizations-api/treemap.md
new file mode 100644
index 0000000000000..9597386bafdf0
--- /dev/null
+++ b/docs/reference/visualizations-api/treemap.md
@@ -0,0 +1,23 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Treemap
+---
+
+# Treemap chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/treemap.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
+
+## Legend
+
+:::{csv-include} _tables/treemap-legend.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/waffle.md b/docs/reference/visualizations-api/waffle.md
new file mode 100644
index 0000000000000..64f429187addb
--- /dev/null
+++ b/docs/reference/visualizations-api/waffle.md
@@ -0,0 +1,23 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: Waffle
+---
+
+# Waffle chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/waffle.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
+
+## Legend
+
+:::{csv-include} _tables/waffle-legend.csv
+:::
\ No newline at end of file
diff --git a/docs/reference/visualizations-api/xy.md b/docs/reference/visualizations-api/xy.md
new file mode 100644
index 0000000000000..6dd439a431ef1
--- /dev/null
+++ b/docs/reference/visualizations-api/xy.md
@@ -0,0 +1,57 @@
+---
+applies_to:
+ stack: preview 9.4+
+ serverless: preview
+navigation_title: XY (line, area, bar)
+---
+
+# XY (line, area, bar) chart
+
+Top-level configuration fields.
+
+:::{csv-include} _tables/xy.csv
+:::
+
+## Axis
+
+:::{csv-include} _tables/xy-axis.csv
+:::
+
+## Drilldowns
+
+:::{csv-include} _tables/shared-drilldowns.csv
+:::
+
+## Styling
+
+:::{csv-include} _tables/xy-styling.csv
+:::
+
+## Layers
+
+
+Each entry in the `layers` array must match one of the following types.
+
+
+### Data layer
+
+:::{csv-include} _tables/xy-layer-data.csv
+:::
+
+### Reference line layer
+
+:::{csv-include} _tables/xy-layer-reference-line.csv
+:::
+
+### Annotation layer
+
+:::{csv-include} _tables/xy-layer-annotation.csv
+:::
+
+## Legend
+
+
+### Legend
+
+:::{csv-include} _tables/xy-legend.csv
+:::
\ No newline at end of file
diff --git a/oas_docs/makefile b/oas_docs/makefile
index 2dba31a8ac5be..e83c01fe54414 100644
--- a/oas_docs/makefile
+++ b/oas_docs/makefile
@@ -62,9 +62,10 @@ api-docs-overlay: ## Apply all overlays
@./node_modules/.bin/bump overlay "output/kibana.tmp3.yaml" "overlays/kibana.overlays.shared.yaml" > "output/kibana.tmp4.yaml"
@./node_modules/.bin/bump overlay "output/kibana.tmp4.yaml" "overlays/streams.overlays.yaml" > "output/kibana.tmp5.yaml"
@./node_modules/.bin/bump overlay "output/kibana.tmp5.yaml" "overlays/dashboards.overlays.yaml" > "output/kibana.tmp6.yaml"
+ @./node_modules/.bin/bump overlay "output/kibana.tmp6.yaml" "overlays/visualizations.overlays.yaml" > "output/kibana.tmp7.yaml"
@./node_modules/.bin/redocly bundle output/kibana.serverless.tmp5.yaml --ext yaml -o output/kibana.serverless.yaml
- @./node_modules/.bin/redocly bundle output/kibana.tmp6.yaml --ext yaml -o output/kibana.yaml
- rm output/kibana.tmp*.yaml
+ @./node_modules/.bin/redocly bundle output/kibana.tmp7.yaml --ext yaml -o output/kibana.yaml
+ rm output/kibana.tmp*.yaml 2>/dev/null || true
rm output/kibana.serverless.tmp*.yaml
.PHONY: api-docs-preview
diff --git a/oas_docs/overlays/dashboards.overlays.yaml b/oas_docs/overlays/dashboards.overlays.yaml
index 1dafe32cd9328..4550e67d59a78 100644
--- a/oas_docs/overlays/dashboards.overlays.yaml
+++ b/oas_docs/overlays/dashboards.overlays.yaml
@@ -172,7 +172,7 @@ actions:
To create ES|QL-based charts, embed them inline as `vis` panels and set `data_source.type` to `"esql"`.
- - **`metric` charts**: reference query result columns in `metrics` using `operation: "value"` and `column`:
+ - **`metric` charts**: reference query result columns in `metrics` using `column`:
```json
{
@@ -206,3 +206,1315 @@ actions:
description: Dashboards documentation
url: https://www.elastic.co/docs/explore-analyze/dashboards
x-displayName: Dashboards
+
+ # ──────────────────────────────────────────────
+ # POST /api/dashboards — examples and code samples
+ # ──────────────────────────────────────────────
+ - target: "$.paths['/api/dashboards']['post']"
+ description: Add examples and code samples to create endpoint
+ update:
+ responses:
+ '201':
+ content:
+ application/json:
+ examples:
+ createDashboardResponse:
+ summary: Create dashboard response
+ description: >
+ Response to creating a dashboard. Returns the generated ID, the full dashboard state
+ in `data`, and metadata. Default option values are always returned even when not
+ explicitly set in the request. Panel `id` values are generated automatically.
+ value:
+ id: 3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b
+ data:
+ options:
+ hide_panel_titles: false
+ hide_panel_borders: false
+ use_margins: true
+ auto_apply_filters: true
+ sync_colors: false
+ sync_cursor: true
+ sync_tooltips: false
+ panels:
+ - grid:
+ x: 0
+ y: 0
+ w: 12
+ h: 8
+ config:
+ title: Total requests
+ data_source:
+ type: data_view_spec
+ index_pattern: kibana_sample_data_logs
+ time_field: timestamp
+ type: metric
+ sampling: 1
+ ignore_global_filters: false
+ metrics:
+ - type: primary
+ operation: count
+ empty_as_null: false
+ styling:
+ primary:
+ position: bottom
+ labels:
+ alignment: left
+ value:
+ sizing: auto
+ alignment: right
+ id: a1b2c3d4-0001-4000-8000-000000000001
+ type: vis
+ - grid:
+ x: 12
+ y: 0
+ w: 12
+ h: 8
+ config:
+ title: Average response size
+ data_source:
+ type: data_view_spec
+ index_pattern: kibana_sample_data_logs
+ time_field: timestamp
+ type: metric
+ sampling: 1
+ ignore_global_filters: false
+ metrics:
+ - type: primary
+ operation: average
+ field: bytes
+ styling:
+ primary:
+ position: bottom
+ labels:
+ alignment: left
+ value:
+ sizing: auto
+ alignment: right
+ id: a1b2c3d4-0001-4000-8000-000000000002
+ type: vis
+ - grid:
+ x: 0
+ y: 8
+ w: 24
+ h: 10
+ config:
+ title: Requests over time
+ type: xy
+ layers:
+ - type: line
+ data_source:
+ type: esql
+ query: "FROM kibana_sample_data_logs | STATS count = COUNT() BY @timestamp = BUCKET(@timestamp, 75, ?_tstart, ?_tend)"
+ sampling: 1
+ ignore_global_filters: false
+ x:
+ column: "@timestamp"
+ y:
+ - column: count
+ axis_id: "y"
+ axis:
+ x:
+ title:
+ visible: true
+ ticks:
+ visible: true
+ grid:
+ visible: true
+ domain:
+ type: fit
+ rounding: false
+ labels:
+ orientation: horizontal
+ scale: ordinal
+ y:
+ anchor: start
+ title:
+ visible: true
+ scale: linear
+ ticks:
+ visible: true
+ grid:
+ visible: true
+ domain:
+ type: full
+ rounding: true
+ labels:
+ orientation: horizontal
+ styling:
+ overlays:
+ partial_buckets:
+ visible: false
+ current_time_marker:
+ visible: false
+ interpolation: linear
+ points:
+ visibility: auto
+ legend:
+ visibility: hidden
+ placement: outside
+ position: right
+ layout:
+ type: grid
+ truncate:
+ max_lines: 1
+ id: a1b2c3d4-0001-4000-8000-000000000003
+ type: vis
+ pinned_panels: []
+ title: Web logs overview
+ meta:
+ created_at: "2026-04-13T10:00:00.000Z"
+ managed: false
+ updated_at: "2026-04-13T10:00:00.000Z"
+ version: WzEwMiwxXQ==
+ x-codeSamples:
+ - lang: cURL
+ label: Create a dashboard (simple) - cURL
+ source: |
+ curl -X POST "${KIBANA_URL}/api/dashboards" \
+ -H "Authorization: ApiKey ${API_KEY}" \
+ -H "kbn-xsrf: true" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "title": "Web logs overview",
+ "panels": [
+ {
+ "grid": {
+ "x": 0,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Total requests",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "count"
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 12,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Average response size",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "average",
+ "field": "bytes"
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 0,
+ "y": 8,
+ "w": 24,
+ "h": 10
+ },
+ "type": "vis",
+ "config": {
+ "type": "xy",
+ "title": "Requests over time",
+ "layers": [
+ {
+ "type": "line",
+ "data_source": {
+ "type": "esql",
+ "query": "FROM kibana_sample_data_logs | STATS count = COUNT() BY @timestamp = BUCKET(@timestamp, 75, ?_tstart, ?_tend)"
+ },
+ "x": {
+ "column": "@timestamp"
+ },
+ "y": [
+ {
+ "column": "count"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }'
+ - lang: Console
+ label: Create a dashboard (simple) - Console
+ source: |
+ POST kbn:/api/dashboards
+ {
+ "title": "Web logs overview",
+ "panels": [
+ {
+ "grid": {
+ "x": 0,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Total requests",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "count"
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 12,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Average response size",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "average",
+ "field": "bytes"
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 0,
+ "y": 8,
+ "w": 24,
+ "h": 10
+ },
+ "type": "vis",
+ "config": {
+ "type": "xy",
+ "title": "Requests over time",
+ "layers": [
+ {
+ "type": "line",
+ "data_source": {
+ "type": "esql",
+ "query": "FROM kibana_sample_data_logs | STATS count = COUNT() BY @timestamp = BUCKET(@timestamp, 75, ?_tstart, ?_tend)"
+ },
+ "x": {
+ "column": "@timestamp"
+ },
+ "y": [
+ {
+ "column": "count"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ - lang: cURL
+ label: Create a dashboard (with sections and controls) - cURL
+ source: |
+ curl -X POST "${KIBANA_URL}/api/dashboards" \
+ -H "Authorization: ApiKey ${API_KEY}" \
+ -H "kbn-xsrf: true" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "title": "Operations overview",
+ "time_range": {
+ "from": "now-7d",
+ "to": "now"
+ },
+ "pinned_panels": [
+ {
+ "type": "options_list_control",
+ "width": "medium",
+ "grow": true,
+ "config": {
+ "title": "Response code",
+ "data_view_id": "90943e30-9a47-11e8-b64d-95841ca0b247",
+ "field_name": "response.keyword"
+ }
+ }
+ ],
+ "panels": [
+ {
+ "title": "Key metrics",
+ "collapsed": false,
+ "grid": {
+ "y": 0
+ },
+ "panels": [
+ {
+ "grid": {
+ "x": 0,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Total requests",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "count"
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 12,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Average response size",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "average",
+ "field": "bytes"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "title": "Traffic trends",
+ "collapsed": false,
+ "grid": {
+ "y": 8
+ },
+ "panels": [
+ {
+ "grid": {
+ "x": 0,
+ "y": 0,
+ "w": 24,
+ "h": 10
+ },
+ "type": "vis",
+ "config": {
+ "type": "xy",
+ "layers": [
+ {
+ "type": "line",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "x": {
+ "operation": "date_histogram",
+ "field": "timestamp"
+ },
+ "y": [
+ {
+ "operation": "count"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 24,
+ "y": 0,
+ "w": 24,
+ "h": 10
+ },
+ "type": "vis",
+ "config": {
+ "type": "xy",
+ "layers": [
+ {
+ "type": "line",
+ "data_source": {
+ "type": "esql",
+ "query": "FROM kibana_sample_data_logs | STATS count = COUNT() BY @timestamp = BUCKET(@timestamp, 75, ?_tstart, ?_tend)"
+ },
+ "x": {
+ "column": "@timestamp"
+ },
+ "y": [
+ {
+ "column": "count"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }'
+ - lang: Console
+ label: Create a dashboard (with sections and controls) - Console
+ source: |
+ POST kbn:/api/dashboards
+ {
+ "title": "Operations overview",
+ "time_range": {
+ "from": "now-7d",
+ "to": "now"
+ },
+ "pinned_panels": [
+ {
+ "type": "options_list_control",
+ "width": "medium",
+ "grow": true,
+ "config": {
+ "title": "Response code",
+ "data_view_id": "90943e30-9a47-11e8-b64d-95841ca0b247",
+ "field_name": "response.keyword"
+ }
+ }
+ ],
+ "panels": [
+ {
+ "title": "Key metrics",
+ "collapsed": false,
+ "grid": {
+ "y": 0
+ },
+ "panels": [
+ {
+ "grid": {
+ "x": 0,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Total requests",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "count"
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 12,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Average response size",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "average",
+ "field": "bytes"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "title": "Traffic trends",
+ "collapsed": false,
+ "grid": {
+ "y": 8
+ },
+ "panels": [
+ {
+ "grid": {
+ "x": 0,
+ "y": 0,
+ "w": 24,
+ "h": 10
+ },
+ "type": "vis",
+ "config": {
+ "type": "xy",
+ "layers": [
+ {
+ "type": "line",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "x": {
+ "operation": "date_histogram",
+ "field": "timestamp"
+ },
+ "y": [
+ {
+ "operation": "count"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 24,
+ "y": 0,
+ "w": 24,
+ "h": 10
+ },
+ "type": "vis",
+ "config": {
+ "type": "xy",
+ "layers": [
+ {
+ "type": "line",
+ "data_source": {
+ "type": "esql",
+ "query": "FROM kibana_sample_data_logs | STATS count = COUNT() BY @timestamp = BUCKET(@timestamp, 75, ?_tstart, ?_tend)"
+ },
+ "x": {
+ "column": "@timestamp"
+ },
+ "y": [
+ {
+ "column": "count"
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+
+ # ──────────────────────────────────────────────
+ # GET /api/dashboards — examples and code samples
+ # ──────────────────────────────────────────────
+ - target: "$.paths['/api/dashboards']['get']"
+ description: Add examples and code samples to search endpoint
+ update:
+ responses:
+ '200':
+ content:
+ application/json:
+ examples:
+ searchDashboardsResponse:
+ summary: Search dashboards response
+ description: >
+ Paginated list of dashboard summaries. Each item includes the ID, a subset of
+ dashboard state fields (`title`, `time_range` if set), and metadata. Full panel
+ content is not included — use the GET endpoint to retrieve a specific dashboard.
+ value:
+ dashboards:
+ - id: 3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b
+ data:
+ title: Web logs overview
+ meta:
+ created_at: "2026-04-13T10:00:00.000Z"
+ managed: false
+ updated_at: "2026-04-13T10:00:00.000Z"
+ version: WzEwMiwxXQ==
+ - id: 7f2a1c40-e83b-11ef-a641-7d5b3f9e1c2a
+ data:
+ time_range:
+ from: now-7d
+ to: now
+ title: Operations overview
+ meta:
+ created_at: "2026-04-12T08:00:00.000Z"
+ managed: false
+ updated_at: "2026-04-12T14:22:00.000Z"
+ version: WzEwMSwxXQ==
+ page: 1
+ total: 2
+ x-codeSamples:
+ - lang: cURL
+ label: Search dashboards - cURL
+ source: |
+ curl -X GET "${KIBANA_URL}/api/dashboards?query=web+logs&per_page=10" \
+ -H "Authorization: ApiKey ${API_KEY}"
+ - lang: Console
+ label: Search dashboards - Console
+ source: |
+ GET kbn:/api/dashboards?query=web+logs&per_page=10
+
+ # ──────────────────────────────────────────────
+ # GET /api/dashboards/{id} — examples and code samples
+ # ──────────────────────────────────────────────
+ - target: "$.paths['/api/dashboards/{id}']['get']"
+ description: Add examples and code samples to get endpoint
+ update:
+ responses:
+ '200':
+ content:
+ application/json:
+ examples:
+ getDashboardResponse:
+ summary: Get dashboard response
+ description: >
+ The full dashboard state including all panels, options, and metadata.
+ value:
+ id: 3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b
+ data:
+ options:
+ hide_panel_titles: false
+ hide_panel_borders: false
+ use_margins: true
+ auto_apply_filters: true
+ sync_colors: false
+ sync_cursor: true
+ sync_tooltips: false
+ title: Web logs overview
+ panels:
+ - grid:
+ x: 0
+ y: 0
+ w: 12
+ h: 8
+ config:
+ title: Total requests
+ data_source:
+ type: data_view_spec
+ index_pattern: kibana_sample_data_logs
+ time_field: timestamp
+ type: metric
+ sampling: 1
+ ignore_global_filters: false
+ metrics:
+ - type: primary
+ operation: count
+ empty_as_null: false
+ styling:
+ primary:
+ position: bottom
+ labels:
+ alignment: left
+ value:
+ sizing: auto
+ alignment: right
+ id: a1b2c3d4-0001-4000-8000-000000000001
+ type: vis
+ - grid:
+ x: 12
+ y: 0
+ w: 12
+ h: 8
+ config:
+ title: Average response size
+ data_source:
+ type: data_view_spec
+ index_pattern: kibana_sample_data_logs
+ time_field: timestamp
+ type: metric
+ sampling: 1
+ ignore_global_filters: false
+ metrics:
+ - type: primary
+ operation: average
+ field: bytes
+ styling:
+ primary:
+ position: bottom
+ labels:
+ alignment: left
+ value:
+ sizing: auto
+ alignment: right
+ id: a1b2c3d4-0001-4000-8000-000000000002
+ type: vis
+ - grid:
+ x: 0
+ y: 8
+ w: 24
+ h: 10
+ config:
+ title: Requests over time
+ type: xy
+ layers:
+ - type: line
+ data_source:
+ type: esql
+ query: "FROM kibana_sample_data_logs | STATS count = COUNT() BY @timestamp = BUCKET(@timestamp, 75, ?_tstart, ?_tend)"
+ sampling: 1
+ ignore_global_filters: false
+ x:
+ column: "@timestamp"
+ y:
+ - column: count
+ axis_id: "y"
+ axis:
+ x:
+ title:
+ visible: true
+ ticks:
+ visible: true
+ grid:
+ visible: true
+ domain:
+ type: fit
+ rounding: false
+ labels:
+ orientation: horizontal
+ scale: ordinal
+ y:
+ anchor: start
+ title:
+ visible: true
+ scale: linear
+ ticks:
+ visible: true
+ grid:
+ visible: true
+ domain:
+ type: full
+ rounding: true
+ labels:
+ orientation: horizontal
+ styling:
+ overlays:
+ partial_buckets:
+ visible: false
+ current_time_marker:
+ visible: false
+ interpolation: linear
+ points:
+ visibility: auto
+ legend:
+ visibility: hidden
+ placement: outside
+ position: right
+ layout:
+ type: grid
+ truncate:
+ max_lines: 1
+ id: a1b2c3d4-0001-4000-8000-000000000003
+ type: vis
+ pinned_panels: []
+ meta:
+ created_at: "2026-04-13T10:00:00.000Z"
+ managed: false
+ updated_at: "2026-04-13T10:00:00.000Z"
+ version: WzEwMiwxXQ==
+ x-codeSamples:
+ - lang: cURL
+ label: Get a dashboard - cURL
+ source: |
+ curl -X GET "${KIBANA_URL}/api/dashboards/3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b" \
+ -H "Authorization: ApiKey ${API_KEY}"
+ - lang: Console
+ label: Get a dashboard - Console
+ source: |
+ GET kbn:/api/dashboards/3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b
+
+ # ──────────────────────────────────────────────
+ # PUT /api/dashboards/{id} — examples and code samples
+ # ──────────────────────────────────────────────
+ - target: "$.paths['/api/dashboards/{id}']['put']"
+ description: Add examples and code samples to update endpoint
+ update:
+ responses:
+ '200':
+ content:
+ application/json:
+ examples:
+ updateDashboardResponse:
+ summary: Update dashboard response
+ description: >
+ The complete updated dashboard state after a full replacement. Note that
+ `meta.created_at` is not returned in update responses — use the GET endpoint
+ to retrieve it.
+ value:
+ id: 3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b
+ data:
+ options:
+ hide_panel_titles: false
+ hide_panel_borders: false
+ use_margins: true
+ auto_apply_filters: true
+ sync_colors: false
+ sync_cursor: true
+ sync_tooltips: false
+ panels:
+ - grid:
+ x: 0
+ y: 0
+ w: 12
+ h: 8
+ config:
+ title: Total requests
+ data_source:
+ type: data_view_spec
+ index_pattern: kibana_sample_data_logs
+ time_field: timestamp
+ type: metric
+ sampling: 1
+ ignore_global_filters: false
+ metrics:
+ - type: primary
+ operation: count
+ empty_as_null: false
+ styling:
+ primary:
+ position: bottom
+ labels:
+ alignment: left
+ value:
+ sizing: auto
+ alignment: right
+ id: a1b2c3d4-0001-4000-8000-000000000001
+ type: vis
+ - grid:
+ x: 12
+ y: 0
+ w: 12
+ h: 8
+ config:
+ title: Average response size
+ data_source:
+ type: data_view_spec
+ index_pattern: kibana_sample_data_logs
+ time_field: timestamp
+ type: metric
+ sampling: 1
+ ignore_global_filters: false
+ metrics:
+ - type: primary
+ operation: average
+ field: bytes
+ styling:
+ primary:
+ position: bottom
+ labels:
+ alignment: left
+ value:
+ sizing: auto
+ alignment: right
+ id: a1b2c3d4-0001-4000-8000-000000000002
+ type: vis
+ - grid:
+ x: 0
+ y: 8
+ w: 24
+ h: 10
+ config:
+ title: Requests over time
+ type: xy
+ layers:
+ - type: line
+ data_source:
+ type: esql
+ query: "FROM kibana_sample_data_logs | STATS count = COUNT() BY @timestamp = BUCKET(@timestamp, 75, ?_tstart, ?_tend)"
+ sampling: 1
+ ignore_global_filters: false
+ x:
+ column: "@timestamp"
+ y:
+ - column: count
+ axis_id: "y"
+ axis:
+ x:
+ title:
+ visible: true
+ ticks:
+ visible: true
+ grid:
+ visible: true
+ domain:
+ type: fit
+ rounding: false
+ labels:
+ orientation: horizontal
+ scale: ordinal
+ y:
+ anchor: start
+ title:
+ visible: true
+ scale: linear
+ ticks:
+ visible: true
+ grid:
+ visible: true
+ domain:
+ type: full
+ rounding: true
+ labels:
+ orientation: horizontal
+ styling:
+ overlays:
+ partial_buckets:
+ visible: false
+ current_time_marker:
+ visible: false
+ interpolation: linear
+ points:
+ visibility: auto
+ legend:
+ visibility: hidden
+ placement: outside
+ position: right
+ layout:
+ type: grid
+ truncate:
+ max_lines: 1
+ id: a1b2c3d4-0001-4000-8000-000000000003
+ type: vis
+ - grid:
+ x: 24
+ y: 8
+ w: 24
+ h: 10
+ config:
+ title: Unique visitors
+ data_source:
+ type: data_view_spec
+ index_pattern: kibana_sample_data_logs
+ time_field: timestamp
+ type: metric
+ sampling: 1
+ ignore_global_filters: false
+ metrics:
+ - type: primary
+ operation: unique_count
+ field: clientip
+ empty_as_null: false
+ styling:
+ primary:
+ position: bottom
+ labels:
+ alignment: left
+ value:
+ sizing: auto
+ alignment: right
+ id: a1b2c3d4-0001-4000-8000-000000000004
+ type: vis
+ pinned_panels: []
+ title: Web logs overview
+ meta:
+ managed: false
+ updated_at: "2026-04-13T11:00:00.000Z"
+ version: WzEwMywxXQ==
+ x-codeSamples:
+ - lang: cURL
+ label: Update a dashboard - cURL
+ source: |
+ curl -X PUT "${KIBANA_URL}/api/dashboards/3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b" \
+ -H "Authorization: ApiKey ${API_KEY}" \
+ -H "kbn-xsrf: true" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "title": "Web logs overview",
+ "panels": [
+ {
+ "grid": {
+ "x": 0,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Total requests",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "count"
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 12,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Average response size",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "average",
+ "field": "bytes"
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 0,
+ "y": 8,
+ "w": 24,
+ "h": 10
+ },
+ "type": "vis",
+ "config": {
+ "type": "xy",
+ "title": "Requests over time",
+ "layers": [
+ {
+ "type": "line",
+ "data_source": {
+ "type": "esql",
+ "query": "FROM kibana_sample_data_logs | STATS count = COUNT() BY @timestamp = BUCKET(@timestamp, 75, ?_tstart, ?_tend)"
+ },
+ "x": {
+ "column": "@timestamp"
+ },
+ "y": [
+ {
+ "column": "count"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 24,
+ "y": 8,
+ "w": 24,
+ "h": 10
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Unique visitors",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "unique_count",
+ "field": "clientip"
+ }
+ ]
+ }
+ }
+ ]
+ }'
+ - lang: Console
+ label: Update a dashboard - Console
+ source: |
+ PUT kbn:/api/dashboards/3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b
+ {
+ "title": "Web logs overview",
+ "panels": [
+ {
+ "grid": {
+ "x": 0,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Total requests",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "count"
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 12,
+ "y": 0,
+ "w": 12,
+ "h": 8
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Average response size",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "average",
+ "field": "bytes"
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 0,
+ "y": 8,
+ "w": 24,
+ "h": 10
+ },
+ "type": "vis",
+ "config": {
+ "type": "xy",
+ "title": "Requests over time",
+ "layers": [
+ {
+ "type": "line",
+ "data_source": {
+ "type": "esql",
+ "query": "FROM kibana_sample_data_logs | STATS count = COUNT() BY @timestamp = BUCKET(@timestamp, 75, ?_tstart, ?_tend)"
+ },
+ "x": {
+ "column": "@timestamp"
+ },
+ "y": [
+ {
+ "column": "count"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "grid": {
+ "x": 24,
+ "y": 8,
+ "w": 24,
+ "h": 10
+ },
+ "type": "vis",
+ "config": {
+ "type": "metric",
+ "title": "Unique visitors",
+ "data_source": {
+ "type": "data_view_spec",
+ "index_pattern": "kibana_sample_data_logs",
+ "time_field": "timestamp"
+ },
+ "metrics": [
+ {
+ "type": "primary",
+ "operation": "unique_count",
+ "field": "clientip"
+ }
+ ]
+ }
+ }
+ ]
+ }
+
+ # ──────────────────────────────────────────────
+ # DELETE /api/dashboards/{id} — code samples
+ # ──────────────────────────────────────────────
+ - target: "$.paths['/api/dashboards/{id}']['delete']"
+ description: Add code samples to delete endpoint
+ update:
+ x-codeSamples:
+ - lang: cURL
+ label: Delete a dashboard - cURL
+ source: |
+ curl -X DELETE "${KIBANA_URL}/api/dashboards/3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b" \
+ -H "Authorization: ApiKey ${API_KEY}" \
+ -H "kbn-xsrf: true"
+ - lang: Console
+ label: Delete a dashboard - Console
+ source: |
+ DELETE kbn:/api/dashboards/3c4b8e10-d57a-11ef-9a52-4f3c2a8d0e1b
+
+ # ──────────────────────────────────────────────
+ # Fix required fields incorrectly promoted by schema.maybe()
+ # ──────────────────────────────────────────────
+ - target: "$.components.schemas['Kibana_HTTP_APIs_kbn-dashboard-data']"
+ description: "Remove x-oas-optional fields from required: query, refresh_interval, time_range"
+ update:
+ required:
+ - options
+ - title
diff --git a/oas_docs/overlays/visualizations.overlays.yaml b/oas_docs/overlays/visualizations.overlays.yaml
new file mode 100644
index 0000000000000..944b7043a271a
--- /dev/null
+++ b/oas_docs/overlays/visualizations.overlays.yaml
@@ -0,0 +1,518 @@
+overlay: 1.0.0
+info:
+ title: Overlays for the Visualizations API
+ version: 0.0.1
+actions:
+ - target: $.components.schemas['Kibana_HTTP_APIs_categoricalColorMapping'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_categoricalColorMapping']
+ update:
+ required:
+ - mode
+ - palette
+ - mapping
+ - target: $.components.schemas['Kibana_HTTP_APIs_counterRateOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_counterRateOperation']
+ update:
+ required:
+ - filter
+ - field
+ - operation
+ - target: $.components.schemas['Kibana_HTTP_APIs_countMetricOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_countMetricOperation']
+ update:
+ required:
+ - filter
+ - operation
+ - target: $.components.schemas['Kibana_HTTP_APIs_cumulativeSumOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_cumulativeSumOperation']
+ update:
+ required:
+ - filter
+ - field
+ - operation
+ - target: $.components.schemas['Kibana_HTTP_APIs_datatableESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_datatableESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - target: $.components.schemas['Kibana_HTTP_APIs_datatableESQLMetric'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_datatableESQLMetric']
+ update:
+ required:
+ - column
+ - target: $.components.schemas['Kibana_HTTP_APIs_datatableNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_datatableNoESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metrics
+ - target: $.components.schemas['Kibana_HTTP_APIs_dateHistogramOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_dateHistogramOperation']
+ update:
+ required:
+ - operation
+ - field
+ - target: $.components.schemas['Kibana_HTTP_APIs_differencesOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_differencesOperation']
+ update:
+ required:
+ - filter
+ - operation
+ - of
+ - target: $.components.schemas['Kibana_HTTP_APIs_esqlDataSource'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_esqlDataSource']
+ update:
+ required:
+ - type
+ - target: $.components.schemas['Kibana_HTTP_APIs_filtersOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_filtersOperation']
+ update:
+ required:
+ - operation
+ - filters
+ - target: $.components.schemas['Kibana_HTTP_APIs_formulaOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_formulaOperation']
+ update:
+ required:
+ - operation
+ - formula
+ - filter
+ - target: $.components.schemas['Kibana_HTTP_APIs_gaugeESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_gaugeESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metric
+ - target: $.components.schemas['Kibana_HTTP_APIs_gaugeNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_gaugeNoESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metric
+ - target: $.components.schemas['Kibana_HTTP_APIs_gradientColorMapping'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_gradientColorMapping']
+ update:
+ required:
+ - mode
+ - palette
+ - target: $.components.schemas['Kibana_HTTP_APIs_heatmapAxes'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_heatmapAxes']
+ update:
+ required: []
+ - target: $.components.schemas['Kibana_HTTP_APIs_heatmapESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_heatmapESQL']
+ update:
+ required:
+ - type
+ - x
+ - data_source
+ - metric
+ - target: $.components.schemas['Kibana_HTTP_APIs_heatmapLegend'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_heatmapLegend']
+ update:
+ required: []
+ - target: $.components.schemas['Kibana_HTTP_APIs_heatmapNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_heatmapNoESQL']
+ update:
+ required:
+ - type
+ - x
+ - data_source
+ - metric
+ - target: $.components.schemas['Kibana_HTTP_APIs_histogramOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_histogramOperation']
+ update:
+ required:
+ - operation
+ - field
+ - target: $.components.schemas['Kibana_HTTP_APIs_lastValueOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_lastValueOperation']
+ update:
+ required:
+ - filter
+ - field
+ - operation
+ - time_field
+ - target: $.components.schemas['Kibana_HTTP_APIs_legacyMetricNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_legacyMetricNoESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metric
+ - target: $.components.schemas['Kibana_HTTP_APIs_metricESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_metricESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metrics
+ - target: $.components.schemas['Kibana_HTTP_APIs_metricNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_metricNoESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metrics
+ - target: $.components.schemas['Kibana_HTTP_APIs_minMaxAvgMedianStdDevMetricOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_minMaxAvgMedianStdDevMetricOperation']
+ update:
+ required:
+ - filter
+ - field
+ - operation
+ - target: $.components.schemas['Kibana_HTTP_APIs_mosaicESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_mosaicESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metric
+ - target: $.components.schemas['Kibana_HTTP_APIs_mosaicLegend'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_mosaicLegend']
+ update:
+ required: []
+ - target: $.components.schemas['Kibana_HTTP_APIs_mosaicNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_mosaicNoESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metric
+ - target: $.components.schemas['Kibana_HTTP_APIs_movingAverageOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_movingAverageOperation']
+ update:
+ required:
+ - filter
+ - operation
+ - of
+ - target: $.components.schemas['Kibana_HTTP_APIs_percentileOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_percentileOperation']
+ update:
+ required:
+ - filter
+ - field
+ - operation
+ - target: $.components.schemas['Kibana_HTTP_APIs_percentileRanksOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_percentileRanksOperation']
+ update:
+ required:
+ - filter
+ - field
+ - operation
+ - target: $.components.schemas['Kibana_HTTP_APIs_pieESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_pieESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metrics
+ - target: $.components.schemas['Kibana_HTTP_APIs_pieLegend'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_pieLegend']
+ update:
+ required: []
+ - target: $.components.schemas['Kibana_HTTP_APIs_pieNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_pieNoESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metrics
+ - target: $.components.schemas['Kibana_HTTP_APIs_rangesOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_rangesOperation']
+ update:
+ required:
+ - operation
+ - field
+ - ranges
+ - target: $.components.schemas['Kibana_HTTP_APIs_regionMapESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_regionMapESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metric
+ - region
+ - target: $.components.schemas['Kibana_HTTP_APIs_regionMapNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_regionMapNoESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metric
+ - region
+ - target: $.components.schemas['Kibana_HTTP_APIs_staticOperationDefinition'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_staticOperationDefinition']
+ update:
+ required:
+ - operation
+ - target: $.components.schemas['Kibana_HTTP_APIs_sumMetricOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_sumMetricOperation']
+ update:
+ required:
+ - filter
+ - field
+ - operation
+ - target: $.components.schemas['Kibana_HTTP_APIs_tagcloudESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_tagcloudESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metric
+ - tag_by
+ - target: $.components.schemas['Kibana_HTTP_APIs_tagcloudNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_tagcloudNoESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metric
+ - tag_by
+ - target: $.components.schemas['Kibana_HTTP_APIs_termsOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_termsOperation']
+ update:
+ required:
+ - operation
+ - fields
+ - target: $.components.schemas['Kibana_HTTP_APIs_treemapESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_treemapESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metrics
+ - target: $.components.schemas['Kibana_HTTP_APIs_treemapLegend'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_treemapLegend']
+ update:
+ required: []
+ - target: $.components.schemas['Kibana_HTTP_APIs_treemapNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_treemapNoESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metrics
+ - target: $.components.schemas['Kibana_HTTP_APIs_uniqueCountMetricOperation'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_uniqueCountMetricOperation']
+ update:
+ required:
+ - filter
+ - field
+ - operation
+ - target: $.components.schemas['Kibana_HTTP_APIs_waffleESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_waffleESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metrics
+ - target: $.components.schemas['Kibana_HTTP_APIs_waffleLegend'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_waffleLegend']
+ update:
+ required: []
+ - target: $.components.schemas['Kibana_HTTP_APIs_waffleNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_waffleNoESQL']
+ update:
+ required:
+ - type
+ - data_source
+ - metrics
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyAnnotationManualEvent'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyAnnotationManualEvent']
+ update:
+ required:
+ - type
+ - timestamp
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyAnnotationManualRange'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyAnnotationManualRange']
+ update:
+ required:
+ - type
+ - interval
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyAnnotationQuery'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyAnnotationQuery']
+ update:
+ required:
+ - type
+ - time_field
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyChartESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyChartESQL']
+ update:
+ required:
+ - type
+ - layers
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyChartNoESQL'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyChartNoESQL']
+ update:
+ required:
+ - type
+ - layers
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyLegendOutsideVertical'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyLegendOutsideVertical']
+ update:
+ required: []
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyStyling'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_xyStyling']
+ update:
+ required: []
+ - target: $.components.schemas['Kibana_HTTP_APIs_kbn-dashboard-data'].required
+ description: Remove x-oas-optional / schema.maybe() fields from required
+ remove: true
+ - target: $.components.schemas['Kibana_HTTP_APIs_kbn-dashboard-data']
+ update:
+ required:
+ - options
+ - title
+ - target: $.components.schemas['Kibana_HTTP_APIs_lensPanelFilters']
+ description: Replace internal schema names with a plain description
+ update:
+ description: >-
+ Array of filters applied to the panel. Accepts KQL/Lucene expression filters, filter groups, or raw
+ Elasticsearch DSL filters.
+
+ # ──────────────────────────────────────────────────────────────────────────
+ # Bump.sh simplification — replace the 24-variant anyOf with links to the
+ # schema reference pages. The full field-level reference lives in the kibana
+ # docs; keeping the anyOf here just makes the Bump create/update pages
+ # unusably slow to render.
+ # ──────────────────────────────────────────────────────────────────────────
+ - target: "$.components.schemas['Kibana_HTTP_APIs_lensApiState'].anyOf"
+ description: Remove the 24-variant anyOf — replaced by description with links below
+ remove: true
+ - target: "$.components.schemas['Kibana_HTTP_APIs_lensApiState']"
+ description: Replace complex union with links to per-chart-type schema reference pages
+ update:
+ type: object
+ additionalProperties: true
+ description: |
+ Chart type configuration. One of 12 chart types — see the schema reference for full field details:
+
+ - [Metric](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/metric)
+ - [XY — line, area, bar](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/xy)
+ - [Gauge](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/gauge)
+ - [Heatmap](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/heatmap)
+ - [Data table](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/datatable)
+ - [Pie / donut](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/pie)
+ - [Mosaic](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/mosaic)
+ - [Treemap](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/treemap)
+ - [Waffle](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/waffle)
+ - [Tag cloud](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/tagcloud)
+ - [Region map](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/region-map)
+ - [Legacy metric](https://www.elastic.co/docs/reference/kibana/reference/visualizations-api/legacy-metric)
diff --git a/oas_docs/scripts/generate_csv_schemas.js b/oas_docs/scripts/generate_csv_schemas.js
new file mode 100644
index 0000000000000..7348240cf1b0c
--- /dev/null
+++ b/oas_docs/scripts/generate_csv_schemas.js
@@ -0,0 +1,598 @@
+#!/usr/bin/env node
+/**
+ * Generates per-chart-type CSV reference tables and .md pages for the
+ * Visualizations API schema reference.
+ *
+ * Output structure:
+ * docs/reference/visualizations-api/
+ * metric.md, xy.md, ... one page per chart type
+ * _tables/
+ * metric.csv top-level fields only (depth 0)
+ * metric-styling.csv sub-schema tables
+ * shared-drilldowns.csv shared across all chart types
+ * xy.csv, xy-styling.csv, ...
+ *
+ * Usage:
+ * node scripts/generate_csv_schemas.js [--input ] [--output ]
+ */
+
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const yaml = require('js-yaml');
+
+const oasDocsDir = path.join(__dirname, '..');
+const kibanaRoot = path.join(oasDocsDir, '..');
+
+function getArg(name, defaultVal) {
+ const i = process.argv.indexOf(name);
+ return i !== -1 ? process.argv[i + 1] : defaultVal;
+}
+
+const inputPath = getArg('--input', path.join(oasDocsDir, 'output/kibana.yaml'));
+const outputDir = getArg('--output', path.join(kibanaRoot, 'docs/reference/visualizations-api'));
+const tablesDir = path.join(outputDir, '_tables');
+
+// ─── Chart type definitions ───────────────────────────────────────────────────
+
+const CHART_TYPES = [
+ { file: 'metric', label: 'Metric', noESQL: 'metricNoESQL', esql: 'metricESQL' },
+ { file: 'xy', label: 'XY (line, area, bar)', noESQL: 'xyChartNoESQL', esql: 'xyChartESQL' },
+ { file: 'gauge', label: 'Gauge', noESQL: 'gaugeNoESQL', esql: 'gaugeESQL' },
+ { file: 'heatmap', label: 'Heatmap', noESQL: 'heatmapNoESQL', esql: 'heatmapESQL' },
+ { file: 'tagcloud', label: 'Tag cloud', noESQL: 'tagcloudNoESQL', esql: 'tagcloudESQL' },
+ { file: 'region-map', label: 'Region map', noESQL: 'regionMapNoESQL', esql: 'regionMapESQL' },
+ { file: 'datatable', label: 'Data table', noESQL: 'datatableNoESQL', esql: 'datatableESQL' },
+ { file: 'pie', label: 'Pie / donut', noESQL: 'pieNoESQL', esql: 'pieESQL' },
+ { file: 'mosaic', label: 'Mosaic', noESQL: 'mosaicNoESQL', esql: 'mosaicESQL' },
+ { file: 'treemap', label: 'Treemap', noESQL: 'treemapNoESQL', esql: 'treemapESQL' },
+ { file: 'waffle', label: 'Waffle', noESQL: 'waffleNoESQL', esql: 'waffleESQL' },
+ { file: 'legacy-metric', label: 'Legacy metric', noESQL: 'legacyMetricNoESQL', esql: null },
+];
+
+// Extra sub-tables specific to the XY chart (layers + legend).
+// Listed here because they come from xyLayersNoESQL anyOf branches, not direct properties.
+const XY_EXTRA_SUB_TABLES = [
+ {
+ label: 'Data layer',
+ csvFile: 'xy-layer-data.csv',
+ variants: ['xyLayerNoESQL', 'xyLayerESQL'],
+ },
+ {
+ label: 'Reference line layer',
+ csvFile: 'xy-layer-reference-line.csv',
+ variants: ['xyReferenceLineLayerNoESQL'],
+ },
+ {
+ label: 'Annotation layer',
+ csvFile: 'xy-layer-annotation.csv',
+ variants: ['xyAnnotationLayerNoESQL'],
+ },
+ {
+ label: 'Legend',
+ csvFile: 'xy-legend.csv',
+ variants: ['xyLegendOutsideHorizontal', 'xyLegendOutsideVertical', 'xyLegendInside'],
+ mergeNotes: { size: 'Outside vertical legend only.', columns: 'Inside legend only.' },
+ },
+];
+
+// ─── OAS loading ──────────────────────────────────────────────────────────────
+
+console.log(`Reading OAS: ${inputPath}`);
+const oas = yaml.load(fs.readFileSync(inputPath, 'utf8'));
+const schemas = oas.components.schemas;
+const OAS_PREFIX = 'Kibana_HTTP_APIs_';
+
+// ─── Reference resolution ─────────────────────────────────────────────────────
+
+function resolveRef(ref) {
+ if (!ref.startsWith('#/')) throw new Error(`External $ref not supported: ${ref}`);
+ const parts = ref.slice(2).split('/');
+ let node = oas;
+ for (const part of parts) {
+ node = node[part.replace(/~1/g, '/').replace(/~0/g, '~')];
+ if (node === undefined) throw new Error(`Cannot resolve: ${ref}`);
+ }
+ return node;
+}
+
+function resolve(schema) {
+ return schema.$ref ? resolveRef(schema.$ref) : schema;
+}
+
+function mergeAllOf(schema) {
+ if (!schema.allOf) return schema;
+ const merged = { properties: {}, required: [] };
+ for (const sub of schema.allOf) {
+ const r = resolve(sub);
+ const e = mergeAllOf(r);
+ Object.assign(merged.properties, e.properties || {});
+ merged.required.push(...(e.required || []));
+ if (!merged.description && e.description) merged.description = e.description;
+ }
+ return merged;
+}
+
+function effective(schema) {
+ return mergeAllOf(resolve(schema));
+}
+
+// ─── Type label ───────────────────────────────────────────────────────────────
+
+function shortName(name) {
+ return name.replace(OAS_PREFIX, '');
+}
+
+function flattenUnion(arr) {
+ const result = [];
+ for (const s of arr) {
+ if (s.anyOf) result.push(...flattenUnion(s.anyOf));
+ else if (s.oneOf) result.push(...flattenUnion(s.oneOf));
+ else result.push(s);
+ }
+ return result;
+}
+
+/**
+ * Merge properties from multiple inline object variants.
+ * For enum fields that differ across variants (like a `type` discriminator),
+ * combines all enum values rather than overwriting.
+ */
+function mergeInlineObjectProps(variants) {
+ const result = {};
+ for (const v of variants) {
+ for (const [k, propSchema] of Object.entries(effective(v).properties || {})) {
+ if (!result[k]) {
+ result[k] = propSchema;
+ } else {
+ // Combine enum values from discriminator fields
+ const existing = result[k];
+ const incoming = propSchema;
+ if (existing.enum && incoming.enum) {
+ result[k] = { ...existing, enum: [...new Set([...existing.enum, ...incoming.enum])] };
+ }
+ // For all other cases keep the first occurrence
+ }
+ }
+ }
+ return result;
+}
+
+/** Simple type category for column 1 (e.g. string, number, boolean, array, object). */
+function typeSimple(schema) {
+ if (!schema) return 'any';
+ if (schema.$ref) return 'object';
+ if (schema.const !== undefined) return typeof schema.const === 'number' ? 'number' : 'string';
+ if (schema.enum) return typeof schema.enum[0] === 'number' ? 'number' : 'string';
+
+ if (schema.anyOf || schema.oneOf) {
+ const flat = flattenUnion(schema.anyOf || schema.oneOf);
+ const types = [...new Set(flat.map(s => {
+ if (s.$ref) return 'object';
+ if (s.properties || s.type === 'object') return 'object';
+ if (s.enum || s.const !== undefined) return typeof (s.enum?.[0] ?? s.const) === 'number' ? 'number' : 'string';
+ return s.type || 'any';
+ }).filter(t => t !== 'null'))];
+ return types.length === 1 ? types[0] : 'any';
+ }
+
+ if (schema.type === 'array') return 'array';
+ return schema.type || 'object';
+}
+
+/** Possible values / item types to surface into column 2. Returns a sentence or null. */
+function metaValues(rawProp) {
+ const s = resolve(rawProp);
+
+ if (s.const !== undefined) return `Value: \`"${s.const}"\``;
+ if (s.enum) return `Possible values: ${s.enum.map(v => typeof v === 'string' ? `\`"${v}"\`` : `\`${v}\``).join(', ')}`;
+
+ if (s.anyOf || s.oneOf) {
+ const flat = flattenUnion(s.anyOf || s.oneOf);
+ // All string enums/consts → list them
+ const enumVals = [];
+ let hasRef = false;
+ for (const item of flat) {
+ if (item.$ref) { hasRef = true; break; }
+ if (item.enum) enumVals.push(...item.enum.map(v => typeof v === 'string' ? `\`"${v}"\`` : `\`${v}\``));
+ else if (item.const !== undefined) enumVals.push(typeof item.const === 'string' ? `\`"${item.const}"\`` : `\`${item.const}\``);
+ }
+ if (!hasRef && enumVals.length > 0) return `Possible values: ${[...new Set(enumVals)].join(', ')}`;
+ // All $refs → list named types
+ const refs = flat.filter(s => s.$ref).map(s => `\`${shortName(s.$ref.split('/').pop())}\``);
+ if (refs.length > 0 && refs.length === flat.length) return `One of: ${[...new Set(refs)].join(', ')}`;
+ // Inline objects with type discriminator
+ const typeEnums = flat.filter(s => !s.$ref && s.properties?.type?.enum)
+ .flatMap(s => s.properties.type.enum.map(v => `\`"${v}"\``));
+ if (typeEnums.length > 0) return `Types: ${[...new Set(typeEnums)].join(', ')}`;
+ }
+
+ // Array items
+ if ((s.type === 'array' || rawProp.type === 'array') && s.items) {
+ const itemsEff = effective(s.items);
+ if (itemsEff.anyOf || itemsEff.oneOf) {
+ const flat = flattenUnion(itemsEff.anyOf || itemsEff.oneOf);
+ const refs = flat.filter(s => s.$ref).map(s => shortName(s.$ref.split('/').pop()));
+ // Skip internal implementation schemas (e.g. kbn-as-code-* generated names)
+ const userFacingRefs = refs.filter(r => !r.includes('_asCode') && !r.startsWith('kbn-as-code-'));
+ if (userFacingRefs.length > 0) return `Items: ${[...new Set(userFacingRefs.map(r => `\`${r}\``))].join(', ')}`;
+ const typeEnums = flat.filter(s => !s.$ref && s.properties?.type?.enum)
+ .flatMap(s => s.properties.type.enum.map(v => `\`"${v}"\``));
+ if (typeEnums.length > 0) return `Types: ${[...new Set(typeEnums)].join(', ')}`;
+ }
+ if (s.items.$ref) {
+ const refName = shortName(s.items.$ref.split('/').pop());
+ if (!refName.includes('_asCode') && !refName.startsWith('kbn-as-code-'))
+ return `Items: \`${refName}\``;
+ }
+ }
+
+ return null;
+}
+
+/** "Default: `value`" string or null. */
+function defaultMeta(schema) {
+ const s = effective(schema);
+ if (s.default === undefined) return null;
+ const v = s.default;
+ return `Default: ${typeof v === 'string' ? `\`"${v}"\`` : `\`${JSON.stringify(v)}\``}`;
+}
+
+/** Build column 1: `name`
_type_
**required** (with optional anchor link on name). */
+function col1(fieldPath, rawProp, required, anchor) {
+ const type = typeSimple(rawProp);
+ const namePart = anchor ? `[\`${fieldPath}\`](${anchor})` : `\`${fieldPath}\``;
+ return [namePart, `_${type}_`, ...(required ? ['**required**'] : [])].join('
');
+}
+
+/** Build column 2: description + possible values + default. */
+function col2(rawProp, baseDescription) {
+ const parts = [];
+ if (baseDescription) {
+ // Ensure description ends with punctuation so the next sentence reads cleanly
+ parts.push(/[.!?]$/.test(baseDescription) ? baseDescription : `${baseDescription}.`);
+ }
+ const values = metaValues(rawProp);
+ if (values) parts.push(`${values}.`);
+ // Skip default annotation if the description already mentions it
+ const def = defaultMeta(rawProp);
+ if (def && !/default/i.test(baseDescription)) parts.push(`${def}.`);
+ return parts.join(' ');
+}
+
+// ─── Schema walker ────────────────────────────────────────────────────────────
+
+/**
+ * Walk a schema's properties and append rows. Stops at depthLimit.
+ * Each row: [col1, col2] (2-column format)
+ * anchorMap: { propName → anchor } for top-level fields that link to sub-sections.
+ */
+function collectRows(schema, parentPath, depth, rows, depthLimit, anchorMap = {}) {
+ if (depth > depthLimit) return;
+
+ const s = effective(schema);
+ const properties = s.properties || {};
+ const requiredSet = new Set(s.required || []);
+
+ for (const [name, rawProp] of Object.entries(properties)) {
+ const fieldPath = parentPath ? `${parentPath}.${name}` : name;
+ const propEff = effective(rawProp);
+
+ const required = requiredSet.has(name);
+ const description = (propEff.description || rawProp.description || '').replace(/\n/g, ' ').trim();
+ const anchor = anchorMap[name] || null;
+
+ rows.push([col1(fieldPath, rawProp, required, anchor), col2(rawProp, description)]);
+
+ // Recurse into plain objects (not unions)
+ if (propEff.properties && !propEff.anyOf && !propEff.oneOf) {
+ collectRows(propEff, fieldPath, depth + 1, rows, depthLimit);
+ }
+
+ // Recurse into array items
+ if ((propEff.type === 'array' || rawProp.type === 'array') && propEff.items) {
+ const itemsEff = effective(propEff.items);
+ if (itemsEff.properties && !itemsEff.anyOf && !itemsEff.oneOf) {
+ collectRows(itemsEff, `${fieldPath}[]`, depth + 1, rows, depthLimit);
+ } else if (itemsEff.anyOf || itemsEff.oneOf) {
+ const variants = flattenUnion(itemsEff.anyOf || itemsEff.oneOf);
+ const inlineObjects = variants.filter(v => !v.$ref && v.properties);
+ if (inlineObjects.length > 0 && inlineObjects.length === variants.length) {
+ const merged = { properties: mergeInlineObjectProps(inlineObjects) };
+ if (Object.keys(merged.properties).length > 0) {
+ collectRows(merged, `${fieldPath}[]`, depth + 1, rows, depthLimit);
+ }
+ }
+ }
+ }
+ }
+}
+
+// ─── Section anchors ─────────────────────────────────────────────────────────
+
+/** Convert a section label to a URL anchor. */
+function toAnchor(label) {
+ return '#' + label.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
+}
+
+// ─── Top-level rows (depth 0, links to sub-table sections) ───────────────────
+
+function mergedTopLevelRows(noESQLSchema, esqlSchema, anchorMap = {}) {
+ const s = effective(noESQLSchema);
+ const requiredSet = new Set(s.required || []);
+ const esqlDS = esqlSchema?.properties?.data_source;
+
+ return Object.entries(s.properties || {}).map(([name, rawProp]) => {
+ const propEff = effective(rawProp);
+ const required = requiredSet.has(name);
+ const description = (propEff.description || rawProp.description || '').replace(/\n/g, ' ').trim();
+
+ // For data_source, combine NoESQL + ES|QL variants into one anyOf for metaValues
+ let displayProp = rawProp;
+ if (name === 'data_source' && esqlDS) {
+ const noEsqlFlat = (rawProp.anyOf || rawProp.oneOf) ? flattenUnion(rawProp.anyOf || rawProp.oneOf) : (rawProp.$ref ? [rawProp] : []);
+ const esqlFlat = (esqlDS.anyOf || esqlDS.oneOf) ? flattenUnion(esqlDS.anyOf || esqlDS.oneOf) : (esqlDS.$ref ? [esqlDS] : []);
+ displayProp = { anyOf: [...noEsqlFlat, ...esqlFlat] };
+ }
+
+ const anchor = anchorMap[name] || null;
+ const c1 = col1(name, displayProp, required, anchor);
+
+ let c2 = col2(displayProp, description);
+ if (name === 'query') {
+ c2 = c2 ? `${c2} Not used with ES|QL data sources.` : 'Not used with ES|QL data sources.';
+ }
+
+ return [c1, c2];
+ });
+}
+
+// ─── Sub-table detection ──────────────────────────────────────────────────────
+
+/**
+ * Returns true if a schema has at least one nested level of properties,
+ * i.e. at least one direct property is itself a $ref or has sub-properties.
+ */
+function hasNestedProps(schema) {
+ return Object.values(schema.properties || {}).some(p => {
+ const e = effective(p);
+ return (e.properties && Object.keys(e.properties).length > 0) || e.anyOf || e.oneOf || e.allOf;
+ });
+}
+
+/**
+ * Scan a chart schema's direct properties for candidates worth a sub-table.
+ * A property qualifies if the resolved type:
+ * - has >= 4 direct properties, OR
+ * - has >= 1 direct property AND at least one nested level
+ * Returns [{ propName, label, csvFile, schema, depthLimit }]
+ */
+function detectSubTables(chartFile, noESQLSchemaName, sharedCsvFiles) {
+ const chartSchema = schemas[OAS_PREFIX + noESQLSchemaName];
+ const s = effective(chartSchema);
+ const subTables = [];
+
+ for (const [propName, rawProp] of Object.entries(s.properties || {})) {
+ // Case 1: direct $ref to a named type
+ if (rawProp.$ref) {
+ const refSchemaName = rawProp.$ref.split('/').pop();
+ const refSchema = schemas[refSchemaName];
+ if (!refSchema) continue;
+
+ const refEff = effective(refSchema);
+ const topCount = Object.keys(refEff.properties || {}).length;
+ if (refEff.anyOf || refEff.oneOf) continue; // union types shown inline
+
+ const worthy = topCount >= 4 || (topCount >= 1 && hasNestedProps(refEff));
+ if (!worthy) continue;
+
+ subTables.push({
+ propName,
+ label: propName.replace(/_/g, ' ').replace(/^(.)/, c => c.toUpperCase()),
+ csvFile: `${chartFile}-${propName}.csv`,
+ schema: refEff,
+ depthLimit: 2,
+ });
+ }
+
+ // Case 2: array with inline-object items (like drilldowns)
+ const propEff = effective(rawProp);
+ if ((propEff.type === 'array' || rawProp.type === 'array') && propEff.items) {
+ const itemsEff = effective(propEff.items);
+ const unionArr = itemsEff.anyOf || itemsEff.oneOf;
+ if (!unionArr) continue;
+
+ const variants = flattenUnion(unionArr);
+ const inlineObjects = variants.filter(v => !v.$ref && v.properties);
+ if (inlineObjects.length === 0 || inlineObjects.length !== variants.length) continue;
+
+ const merged = { properties: mergeInlineObjectProps(inlineObjects) };
+ if (Object.keys(merged.properties).length < 3) continue;
+
+ // Use a shared CSV for types that appear identically across all charts (drilldowns)
+ const csvFile = propName === 'drilldowns'
+ ? 'shared-drilldowns.csv'
+ : `${chartFile}-${propName}.csv`;
+
+ const isNew = !subTables.some(t => t.csvFile === csvFile);
+ if (isNew) {
+ subTables.push({
+ propName,
+ label: propName.replace(/_/g, ' ').replace(/^(.)/, c => c.toUpperCase()),
+ csvFile,
+ schema: merged,
+ depthLimit: 1,
+ shared: csvFile.startsWith('shared-'),
+ });
+ }
+ }
+ }
+
+ return subTables;
+}
+
+// ─── Multi-variant merge (legend variants, etc.) ──────────────────────────────
+
+function mergedVariantRows(variantSchemas, mergeNotes = {}, depthLimit = 2) {
+ const allRows = [];
+ const seenKeys = new Set();
+
+ for (const schema of variantSchemas) {
+ const rows = [];
+ collectRows(schema, '', 0, rows, depthLimit);
+ for (const row of rows) {
+ const fieldName = row[0].match(/`([^`]+)`/)?.[1] ?? row[0];
+ if (!seenKeys.has(fieldName)) {
+ seenKeys.add(fieldName);
+ allRows.push(row);
+ }
+ }
+ }
+
+ return allRows.map(([c1, c2]) => {
+ const fieldName = c1.match(/`([^`]+)`/)?.[1] ?? '';
+ if (mergeNotes[fieldName]) {
+ return [c1, c2 ? `${c2} ${mergeNotes[fieldName]}` : mergeNotes[fieldName]];
+ }
+ return [c1, c2];
+ });
+}
+
+// ─── CSV serialization ────────────────────────────────────────────────────────
+
+function toCsv(rows) {
+ const HEADER = ['Parameter', 'Description'];
+ return [HEADER, ...rows]
+ .map(row =>
+ row.map(cell => {
+ const s = String(cell ?? '');
+ return s.includes(',') || s.includes('"') || s.includes('\n')
+ ? `"${s.replace(/"/g, '""')}"`
+ : s;
+ }).join(',')
+ )
+ .join('\n');
+}
+
+function writeTable(csvFile, rows) {
+ const filePath = path.join(tablesDir, csvFile);
+ fs.writeFileSync(filePath, toCsv(rows));
+ console.log(` ${csvFile}: ${rows.length} fields`);
+}
+
+// ─── Markdown page generation ─────────────────────────────────────────────────
+
+function mdPage(chartType, subTables) {
+ const { file, label } = chartType;
+ const lines = [
+ `---`,
+ `applies_to:`,
+ ` stack: preview 9.4+`,
+ ` serverless: preview`,
+ `navigation_title: ${label}`,
+ `---`,
+ ``,
+ `# ${label} chart`,
+ ``,
+ `Top-level configuration fields.`,
+ ``,
+ `:::{csv-include} _tables/${file}.csv`,
+ `:::`,
+ ];
+
+ const regularSubs = subTables.filter(t => !t.isExtraSection);
+ const extraSections = subTables.filter(t => t.isExtraSection);
+
+ for (const sub of regularSubs) {
+ lines.push('', `## ${sub.label}`, '');
+ lines.push(`:::{csv-include} _tables/${sub.csvFile}`, `:::`);
+ }
+
+ for (const section of extraSections) {
+ lines.push('', `## ${section.label}`, '');
+ if (section.intro) lines.push('', section.intro, '');
+ for (const item of section.items) {
+ lines.push('', `### ${item.label}`, '');
+ lines.push(`:::{csv-include} _tables/${item.csvFile}`, `:::`);
+ }
+ }
+
+ return lines.join('\n');
+}
+
+// ─── Main ─────────────────────────────────────────────────────────────────────
+
+fs.mkdirSync(tablesDir, { recursive: true });
+
+const sharedCsvFiles = new Set();
+
+for (const chartType of CHART_TYPES) {
+ const { file, label, noESQL, esql } = chartType;
+ console.log(`\n${label}:`);
+
+ const noESQLSchema = schemas[OAS_PREFIX + noESQL];
+ const esqlSchema = esql ? schemas[OAS_PREFIX + esql] : null;
+ if (!noESQLSchema) { console.warn(` Skipping: schema not found for ${noESQL}`); continue; }
+
+ // 2. Auto-detected sub-tables (needed before top-level CSV for anchor map)
+ const subTables = detectSubTables(file, noESQL, sharedCsvFiles);
+ for (const sub of subTables) {
+ if (sub.shared && sharedCsvFiles.has(sub.csvFile)) continue; // write CSV once
+ const rows = [];
+ collectRows(sub.schema, '', 0, rows, sub.depthLimit);
+ writeTable(sub.csvFile, rows);
+ if (sub.shared) sharedCsvFiles.add(sub.csvFile);
+ }
+
+ // Build anchor map: propName → section anchor on this page
+ const anchorMap = {};
+ for (const sub of subTables) {
+ if (sub.propName) anchorMap[sub.propName] = toAnchor(sub.label);
+ }
+ // XY: layers and legend come from extra sub-tables, not detectSubTables
+ if (file === 'xy') {
+ anchorMap['layers'] = '#layers';
+ anchorMap['legend'] = '#legend';
+ }
+
+ // 1. Top-level table (with links to sub-table sections)
+ writeTable(`${file}.csv`, mergedTopLevelRows(noESQLSchema, esqlSchema, anchorMap));
+
+ // 3. XY-specific extra sub-tables
+ let extraSections = [];
+ if (file === 'xy') {
+ const layerItems = [];
+ for (const extra of XY_EXTRA_SUB_TABLES) {
+ const variantSchemas = extra.variants
+ .map(n => schemas[OAS_PREFIX + n])
+ .filter(Boolean);
+ if (variantSchemas.length === 0) continue;
+ const rows = mergedVariantRows(variantSchemas, extra.mergeNotes || {}, 2);
+ writeTable(extra.csvFile, rows);
+ layerItems.push({ label: extra.label, csvFile: extra.csvFile });
+ }
+ // Split: layers go in one section, legend in another
+ const layerSection = {
+ label: 'Layers',
+ intro: 'Each entry in the `layers` array must match one of the following types.',
+ items: layerItems.filter(i => i.csvFile.includes('layer')),
+ isExtraSection: true,
+ };
+ const legendSection = {
+ label: 'Legend',
+ items: layerItems.filter(i => i.csvFile.includes('legend')),
+ isExtraSection: true,
+ };
+ extraSections = [layerSection, legendSection].filter(s => s.items.length > 0);
+ }
+
+ // 4. Generate .md page
+ const allSubs = [...subTables, ...extraSections];
+ const mdContent = mdPage(chartType, allSubs);
+ const mdPath = path.join(outputDir, `${file}.md`);
+ fs.writeFileSync(mdPath, mdContent);
+ console.log(` → ${file}.md`);
+}
+
+console.log('\nDone. Files written to:', outputDir);