---
title: "Get Spreadsheet Version"
method: GET
path: "/api/v1/spreadsheets/{spreadsheet_id}/versions/{version_number}"
tags: ["spreadsheets"]
---

# Get Spreadsheet Version

`GET /api/v1/spreadsheets/{spreadsheet_id}/versions/{version_number}`

One version's body, evaluated against its own provenance or not at all.

## Path parameters

- `spreadsheet_id` string, uuid, required
- `version_number` integer, required

## Response `200`

Successful Response

- SpreadsheetVersionRead — One stored version of a spreadsheet. Not a ``DocVersionRead``: a doc's ``body`` is markdown and a spreadsheet's is a workbook, so the two share a name and nothing else. ``body`` is always what that version held. ``sources`` and ``sheets`` are present only when the version recorded its own provenance: evaluating an old body against today's runs would show today's figures in the grid a person is reading as history. With nothing recorded there is nothing truthful to evaluate, so the caller renders the cells' text and says so.
  - `version_number` integer, required
  - `created_by` 'agent' | 'user', required
  - `created_by_user_id` string, uuid, nullable, required
  - `created_at` string, date-time, required
  - `provenance` object, nullable, required
  - `body` SpreadsheetBodyOutput, required
    - `sources` WidgetBlockOutput[]
      - `id` string, required
      - `title` string, nullable
      - `viz_type` 'line' | 'bar' | 'pie' | 'number' | 'pivot' | 'logs_table' | 'heatmap', required
      - `config` WidgetConfig, required — The query half of a persisted ``Widget.config``. A widget embeds a ``ReportQuery`` rather than naming one, so this is that same query MINUS its identity (``key``/``version``) and minus ``time_dimension`` (which the referenced measures own and import validation pins — a widget never restates it). Same field TYPES throughout, so the two cannot describe different vocabularies. ``extra="allow"`` is deliberate and is the line this model draws: * The QUERY half is strict. A bad ``group_by`` or an unresolvable ref produces a wrong number silently, on a human's screen, inside a report an agent said was correct. That must fail at the boundary. * PRESENTATION keys (``show_na``, ``table_orientation``, ``bar_mode``, ``row_groups``, ``column_colors``, ``group_colors``, ``sticky_first_column``, ``legendPosition`` — note the one camelCase legacy key) ride along unmodelled. Locking them down would 422 real stored configs to protect against a wrong COLOUR, which is not the failure mode worth guarding. Near-miss metric keys (``metric_ref`` for ``metric_refs``) and catalog-shaped entries are coerced by :func:`coerce_widget_config_metric_refs` before the query half is validated, so a find_metric hit pasted verbatim is accepted. Nested ``config.metric_refs`` / ``spec.metric_refs`` hoist the same way (#2457). A config that still has no metrics after that rewrite is rejected.
        - `metric_refs` MetricRef[]
          - `key` string, required
          - `category` 'standard' | 'custom' | 'formula', required
          - `outcome` 'pass' | 'fail', nullable
          - `label` string, nullable
          - `is_after_hours` boolean, nullable
          - `population_filters` string[], nullable
          - `buckets` AttributionBucket[], nullable
          - `view_id` string, uuid, nullable
        - `aggregation` AggregationSpec — How to aggregate the metric values.
          - `type` 'count' | 'sum' | 'avg' | 'pass_rate' | 'breakdown'
          - `period` 'minute' | 'hour' | 'day' | 'week' | 'month' | 'total' | 'auto'
        - `group_by` string[]
        - `filters` WidgetIntrinsicFilters — The filters a saved query OWNS — ``WidgetFilters`` minus the globals and the date range. Widget-intrinsic filters describe what the widget *is* ("bookings only", "AI-handled conversations"); global dimensions describe what the viewer is currently looking at. Only the first kind may be persisted. Field-for-field equality with ``WidgetFilters`` (minus ``GLOBAL_DIM_KEYS`` and ``TIME_RESOLUTION_KEYS``) is asserted by ``test_intrinsic_filters_match_widget_filters`` — written out explicitly rather than derived with ``create_model`` because a dynamically built model is not statically typeable, and this file is the contract an agent reads out of the OpenAPI schema. ``extra="forbid"`` is the point of the model, not a detail. Pydantic's default would IGNORE a global dimension passed here — silently dropping it, which is the same failure mode as the client's ``console.warn`` strip: the caller believes the filter was saved and it was not. An agent especially must be told, so it learns the rule instead of re-sending it.
          - `matrix_agent_refs` string[], nullable
          - `buckets` AttributionBucket[], nullable
          - `dispositions` string[], nullable
          - `was_transferred` boolean, nullable
          - `transfer_failed` boolean, nullable
          - `has_failed_transfer_attempt` boolean, nullable
          - `was_missed` boolean, nullable
          - `was_answered` boolean, nullable
          - `is_after_hours` boolean, nullable
          - `population_filters` string[], nullable
          - `is_voicemail` boolean, nullable
          - `has_first_response` boolean, nullable
          - `has_ended` boolean, nullable
          - `transfer_destination` string, nullable
          - `transfer_desk` string, nullable
          - `failed_transfer_desk` string, nullable
          - `country` string, nullable
          - `selected_language` string, nullable
          - `language_selection_source` string, nullable
          - `countries` string[], nullable
          - `countries_exclude` string[], nullable
          - `outcome` string, nullable
          - `has_booking_intent` boolean, nullable
          - `has_booking` boolean, nullable
          - `booking_made` boolean, nullable
          - `local_hour_of_day` integer, nullable
          - `local_day_of_week` integer, nullable
          - `tag_ids` string[], nullable
          - `attribution_tiers` string[], nullable
          - `metric_filters` MetricFilter[], nullable
            - `metric_id` string, required
            - `outcome` 'pass' | 'fail' | 'any' | 'na', nullable
            - `categorical_value` string, nullable
          - `standard_filters` StandardColumnFilter[], nullable
            - `field` string, required
            - `value` string, required
        - `result_shape` 'scalar' | 'timeseries' | 'breakdown' | 'pivot' | 'matrix' | 'records', nullable
        - `metric_filters` MetricFilter[]
          - `metric_id` string, required
          - `outcome` 'pass' | 'fail' | 'any' | 'na', nullable
          - `categorical_value` string, nullable
        - `matrix_agent_refs` string[]
        - `sources` string[], nullable
        - `records_subject` 'voice' | 'text' | 'email' | 'booking', nullable
        - `column_rollups` ColumnRollup[]
          - `label` string, required
          - `buckets` AttributionBucket[], required
      - `start_date` string, date, required
      - `end_date` string, date, required
      - `property_ids` string[], nullable
      - `property_ids_exclude` string[], nullable
    - `sheets` SpreadsheetSheet[], required
      - `name` string, required
      - `cells` object
      - `column_widths` object
  - `sources` object, nullable, required
  - `sheets` EvaluatedSheetRead[], nullable, required
    - `name` string, required
    - `cells` object, required

## Other responses

- `403` — Error
- `404` — Error
- `422` — Validation Error

## Changes

> 28 revisions in range; 1 not diffed.

- **2026-09-24** `4df7b22a7dae` — 1 info
  - removed the `transfer_target` enum value from the `body/sources/items/config/group_by/items/` response property for the response status `200`
- **2026-09-23** `ae7627cd46da` — 2 warning, 4 info
  - added the new `booking` enum value to the `body/sources/items/config/sources/anyOf[subschema #1]/items/` response property for the response status `200`
  - added the new `transfer_desk` enum value to the `body/sources/items/config/group_by/items/` response property for the response status `200`
  - added the optional property `body/sources/items/config/filters/anyOf[subschema #1: WidgetIntrinsicFilters]/failed_transfer_desk` to the response with the `200` status
  - added the optional property `body/sources/items/config/filters/anyOf[subschema #1: WidgetIntrinsicFilters]/has_failed_transfer_attempt` to the response with the `200` status
  - …2 more
- **2026-09-20** `dabff0d3c9ff` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/getanana/apis/cleon-api/changes/api/v1/spreadsheets/:spreadsheet_id/versions/:version_number/get.md)

---

[API](https://skmtc.dev/getanana/apis/cleon-api.md) · [All operations](https://skmtc.dev/getanana/apis/cleon-api/llms.txt) · [OpenAPI document](https://skmtc.dev/getanana/apis/cleon-api/revisions/6b978a517225?raw)
