---
title: "Render AI job visualization"
method: GET
path: "/api/v1/ai/jobs/{jobId}/vis"
tags: ["AI"]
---

# Render AI job visualization

`GET /api/v1/ai/jobs/{jobId}/vis`

## Request

- Base URL: `https://omniapp.co` (the document declares no server; this is the origin it was published from)
- URL: `https://omniapp.co/api/v1/ai/jobs/{jobId}/vis`
- Auth: HTTP bearer

Render the visualization from a completed AI job as a PNG image. The endpoint extracts the visualization configuration from the job result, loads Arrow IPC data, and renders it server-side using Vega. For style-only follow-ups (e.g., "make it a bar chart"), the endpoint walks back through previous jobs in the conversation to find the original query data.

## Path parameters

- `jobId` string, uuid, required: The unique identifier of the AI job

## Query parameters

- `auto_vis` string: The literal string false disables automatic chart selection and returns 204 if the job has no visualization. All other strings, including empty strings and 0, enable automatic selection. Defaults to true.
- `debug` string: Returns debug JSON instead of the rendered PNG when enabled. The strings false, 0, and the empty string disable debug; other nonempty strings, including true and 1, enable it. Defaults to false.

## Response `200`

Visualization rendered as image/png, or application/json when debug is enabled. Debug JSON contains the compiled visualization and query details, or diagnostic information when Arrow data is unavailable.

- union
  - object
    - `compiledVegaLiteSpec` object, nullable, required: Compiled Vega specification, or null if compilation produced no specification.
    - `dataSource` 'arrow', required
    - `hasQuery` true, required
    - `hasResultFields` true, required
    - `query` OmniQuery, required
      - `aiGenerated` boolean: True when AI generated this query’s SQL; the AI SQL is shown in the advanced SQL box.
      - `branch_id` string: Branch model ID when querying against a model branch.
      - `calculations` object[], required: Table calculations attached to this query.
        - `allow_refs_to_unselected_fields` boolean: Set by the Kotlin parser when this calc references fields not selected at the top level (AI SQL-gen produces these; UI-authored calcs do not).
        - `anchor_row` integer: The 1-indexed row the Excel-style formula was authored against, so row-relative references resolve to the intended offsets — e.g. a percent-of-previous "=B4/B5" authored on row 5. Only used when sql_expression is omitted; defaults to 1 (B1 = this row).
        - `calc_name` string, required: Internal identifier for the calculation, used as the column alias.
        - `description` string: Description of the calculation.
        - `format` string: Number/date format string (e.g. "#,##0.00").
        - `label` string: Display label shown in the UI.
        - `original_formula` string: The calculation formula. Prefer referencing selected fields by name (e.g. "=SUM(orders.revenue)") — name references are stable under column reordering and have no row semantics. Excel-style references also work: column letters map to the query's fields in table order (A = the first field) with rows anchored at row 1 (set anchor_row to author row-relative references against a different row), and calc columns can only be referenced by letter. When sql_expression is omitted, the server parses this formula and rejects the request if it does not parse.
        - `outside_pivot` boolean: When true, the calculation is evaluated outside the pivot grouping.
        - `pushdown` boolean, nullable: Per-calc override for whether to evaluate before the row limit. `null` defers to the model-level default.
        - `sql` string: Compiled SQL string produced from the formula.
        - `sql_expression` unknown
        - `swallow_errors` boolean: Defaults to true when omitted on write, matching calculations created in the Omni UI. When true, a calculation that errors shows #ERROR! in its cells instead of failing the whole query, and spreadsheet-style calculations are evaluated by Omni rather than pushed to the database. Set false to fail the query on a calculation error instead.
      - `column_limit` number: Max number of pivot columns to return.
      - `column_totals` object, required: Column-level aggregation totals, keyed by field name.
      - `controls` union[]: Interactive controls (field selectors, PoP controls) attached to this query.
        - union
          - object
            - `description` string
            - `hidden` boolean
            - `id` string, required
            - `label` string
            - `display` 'SELECT' | 'BUTTON_TOGGLE'
            - `field` string, required
            - `kind` 'FIELD' | 'TIMEFRAME', required
            - `options` object[], required
              - …
            - `type` 'FIELD_SELECTION', required
          - object
            - `description` string
            - `hidden` boolean
            - `id` string, required
            - `label` string
            - `display` 'SELECT' | 'BUTTON_TOGGLE'
            - `options` object[], required
              - …
            - `selectionMap` object, required
            - `type` 'MULTI_FIELD_SELECTION', required
            - `value` string
          - object
            - `computations` object[], required
              - …
            - `filterFieldName` string, required
            - `filterId` string
            - `id` string, required
            - `type` 'PERIOD_OVER_PERIOD', required
          - object
            - `description` string
            - `hidden` boolean
            - `id` string, required
            - `label` string
            - `fieldOrder` 'query' | 'control'
            - `options` object[], required
              - …
            - `type` 'FIELD_PICKER', required
            - `values` string[]
          - object
            - `description` string
            - `hidden` boolean
            - `id` string, required
            - `label` string
            - `conjunction` 'OR', required
            - `filters` object[], required
              - …
            - `type` 'MULTI_FIELD_FILTER', required
          - object
            - `description` string
            - `hidden` boolean
            - `id` string, required
            - `label` string
            - `fieldSelection` union, required
              - …
            - `includeViewNameInLabels` boolean
            - `type` 'DYNAMIC_FILTER', required
          - object
            - `description` string
            - `hidden` boolean
            - `id` string, required
            - `label` string
            - `defaultValue` integer, required
            - `field` string, required
            - `max` integer
            - `min` integer
            - `type` 'TOP_N', required
            - `value` integer, required
      - `cube_metadata` object: Cube-specific metadata (topic name, cube name, hash key) for cube-backed queries.
        - `cube_name` string, required
        - `hash_key` string, required
        - `topic_name` string, required
      - `custom_summary_types` object: Per-field custom summary functions (e.g. SUM, AVG, COUNT).
      - `dbtFileName` string: dbt file name when this query is backed by a dbt model.
      - `dbtMode` boolean: Whether this query is in dbt mode.
      - `default_group_by` boolean: When true, all dimensions are implicitly included in GROUP BY, unless the model's default_group_by setting (never or primary_key) suppresses it.
      - `dimensionIndex` number: Index of the primary dimension used for result ordering.
      - `executableSQL` string: Server-compiled SQL string (read-only, set by the backend).
      - `fields` string[], required: Model field names selected for the query (dimensions + measures).
      - `fill_fields` string[], required: Fields whose missing date/time values should be filled with nulls to create continuous series.
      - `filters` object, required: Query filters keyed by filter ID.
      - `filtersUsedInSql` object: Filters that were embedded directly in raw SQL (read-only).
      - `imported_model_ids` string[]: Model IDs (UUIDs) the query reaches through its composite topic's imports — the topic's import_models entries, which must be model ids, not names. Authorization requires the run gate on each; omit only for queries that do not touch imported topics.
      - `join_paths_from_topic_name` string: Name of the topic the query runs on; it also sets join path precedence.
      - `join_via_map` object: Virtual topic join path overrides, keyed by view name to ordered join path.
      - `limit` number: Row limit for the query.
      - `manualSort` boolean: When true, the user has explicitly set a custom sort order.
      - `metadata` object: Per-field display metadata (format, label, order).
      - `modelId` string, required: The model ID this query runs against.
      - `model_extension_id` string: ID of a model extension layered on top of the base model.
      - `offset` number: Row offset for pagination.
      - `parsed` boolean: Whether this raw SQL query has been parsed into a semantic query.
      - `periodOverPeriodTransposed` boolean: Whether the period-over-period comparison columns are transposed.
      - `period_over_period_computations` object[]: Period-over-period comparison configurations.
        - `date_filter_field_name` string, required: The date dimension field this comparison is anchored to.
        - `date_filter_id` string: ID of the date filter being offset (if backed by a dashboard filter).
        - `is_dynamic_previous_period` boolean: When true, the previous period is calculated dynamically relative to the current filter range.
        - `periods_ago` number, nullable, required: How many periods back to compare (e.g. 1 = previous period). Null if not set.
        - `time_unit_name` string, nullable, required: Time grain for the offset (e.g. "month", "year"). Null if not set.
      - `pivots` string[], required: Field names to pivot on (columns become values).
      - `rewriteSql` boolean: When true, the backend should rewrite/optimize the SQL.
      - `row_totals` object, required: Row-level aggregation totals, keyed by field name.
      - `sorts` object[], required: Sort clauses applied to the query result set.
        - `column_name` string, required: The model field name to sort by.
        - `is_column_sort` boolean: When true, this sort targets a pivoted column rather than a row dimension.
        - `pivot_value_map` object: Maps pivot field names to specific pivot values, scoping the sort to a single pivot column.
        - `sort_descending` boolean, required: When true, sort order is descending.
        - `subtotal_sort` string: Field name whose subtotal row should be used as the sort key.
      - `sqlSortsEnabled` boolean: Whether user-created sorts are enabled on a raw SQL query.
      - `staticQueryReferences` object: Pre-computed query references for AI chat context. Inner shape is `OmniQuery & { model_id: string }`; left as `unknown` to avoid a recursive zod schema. Validated structurally by consumers when they execute the referenced sub-queries.
      - `table` string, required: Base view (table) name in the model.
      - `transposed_measures` string[]: Measure field names that have been transposed into rows.
      - `userEditedSQL` string, required: User-authored raw SQL (empty string when not in SQL mode).
      - `version` number: Schema version for migration support.
    - `resultFields` object[], required
      - `label` string, required
      - `name` string, required
      - `type` object: Serialized Apache Arrow data type, when available.
        - `typeId` integer, required
    - `rowCount` integer, required
    - `visConfig` object, required
      - `chartType` 'auto' | 'area' | 'areaStacked' | 'areaStackedPercentage' | 'bar' | 'barLine' | 'barGrouped' | 'barStacked' | 'barStackedPercentage' | 'boxplot' | 'code' | 'column' | 'columnGrouped' | 'columnStacked' | 'columnStackedPercentage' | 'heatmap' | 'kpi' | 'line' | 'lineColor' | 'map' | 'regionMap' | 'markdown' | 'omni-ai-summary-markdown' | 'pie' | 'funnel' | 'sankey' | 'point' | 'pointColor' | 'pointSize' | 'pointSizeColor' | 'singleRecord' | 'omni-spreadsheet' | 'summaryValue' | 'svgMap' | 'table' | 'treemap'
      - `visualization` object
  - object
    - `error` string, required
    - `hasQuery` false, required
    - `jobId` string, uuid, required

## Other responses

- `204`: No visualization exists and auto_vis=false disables automatic chart selection. The response has no body.
- `400`: Invalid job ID format. Must be a valid UUID.
- `401`: Missing or invalid API key.
- `403`: Insufficient permissions. AI query generation must be enabled for the organization and the caller must have permission to use AI on the job's model.
- `404`: Job not found, not in COMPLETE state, or the apiAiVis feature flag is not enabled.
- `422`: The job completed but cannot be rendered as a visualization. Common causes: no visualization action in the job result, no Arrow IPC data available, missing summary fields, or the chart type is not renderable as an image (e.g., tables, KPIs).

## Changes

- **2026-10-16** `12e9325d71cd`: 2 breaking, 2 info
  - added `subschema #1, subschema #2, subschema #3, subschema #4, subschema #5, subschema #6, subschema #7, subschema #8` to the `anyOf[subschema #1]/query/filters/additionalProperties/` response property `oneOf` list for the response status `200`
  - added `subschema #1, subschema #2, subschema #3, subschema #4, subschema #5, subschema #6, subschema #7, subschema #8` to the `anyOf[subschema #1]/query/filtersUsedInSql/additionalProperties/` response property `oneOf` list for the response status `200`
  - removed `subschema #1, subschema #2, subschema #3, subschema #4, subschema #5, subschema #6, subschema #7, subschema #8` from the `anyOf[subschema #1]/query/filters/additionalProperties/` response property `oneOf` list for the response status `200`
  - removed `subschema #1, subschema #2, subschema #3, subschema #4, subschema #5, subschema #6, subschema #7, subschema #8` from the `anyOf[subschema #1]/query/filtersUsedInSql/additionalProperties/` response property `oneOf` list for the response status `200`
- **2026-10-16** `3939110e279b`: 4 info
  - added the new optional `query` request parameter `auto_vis`
  - added the new optional `query` request parameter `debug`
  - added the media type `application/json` for the response with the status `200`
  - added the success response with the status `204`

[Operation history](https://skmtc.dev/omniapp/apis/omni-api/changes/api/v1/ai/jobs/:jobId/vis/get.md)

---

[API](https://skmtc.dev/omniapp/apis/omni-api.md) - [All operations](https://skmtc.dev/omniapp/apis/omni-api/llms.txt) - [OpenAPI document](https://skmtc.dev/omniapp/apis/omni-api/revisions/76b40ebc2292?raw)
