diff --git a/api/openapi.json b/api/openapi.json index 17b7e2e..a5d86d0 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -571,6 +571,11 @@ "additionalProperties": {}, "description": "Query execution results as a JSON object. Only present when runQuery is true (the default) and the query executed successfully. The structure contains the query result data." }, + "rowLimitHit": { + "type": "boolean", + "description": "True when the executed query returned as many rows as its limit allowed, so the result is probably partial and a larger limit in the prompt would return more. Only present when the query ran.", + "example": false + }, "topic": { "type": [ "string", @@ -4391,7 +4396,11 @@ "type": "object", "properties": { "controls": { - "description": "Control configuration object. Keys are control IDs, values contain controlType, filterId, label, etc." + "type": "array", + "items": { + "$ref": "#/components/schemas/DashboardControlEntry" + }, + "description": "Control configuration, ordered. Each entry has an `id` and a CONTROL_TYPE `type`, plus the fields that type carries (a field-selection control its `field` and `options`, a period-over-period one its `computations`, and so on)." }, "filterOrder": { "type": "array", @@ -4405,7 +4414,11 @@ ] }, "filters": { - "description": "Filter configuration object. Keys are filter IDs, values contain fieldName, viewName, kind, defaultValue, etc." + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/DashboardFilterEntry" + }, + "description": "Filter configuration object. Keys are filter IDs; each value has a FILTER_TYPE `type` plus that type's fields — fieldName, viewName, kind, defaultValue, etc." }, "identifier": { "type": "string", @@ -4414,10 +4427,59 @@ } }, "required": [ + "controls", "filterOrder", + "filters", "identifier" ] }, + "DashboardControlEntry": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "FIELD_SELECTION", + "MULTI_FIELD_SELECTION", + "FIELD_PICKER", + "PERIOD_OVER_PERIOD", + "MULTI_FIELD_FILTER", + "DYNAMIC_FILTER", + "TOP_N" + ] + } + }, + "required": [ + "id", + "type" + ], + "additionalProperties": {} + }, + "DashboardFilterEntry": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "string", + "number", + "date", + "boolean", + "null", + "composite", + "query", + "user_attribute" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": {} + }, "DashboardsUpdateFiltersBody": { "type": "object", "properties": { @@ -5651,6 +5713,13 @@ "name": { "type": "string", "description": "Document name." + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Non-blocking warnings — present only when there are any. Currently: external resource hosts the app's iframe CSP will block until an org admin allows them (`app` creates only). The document was still created and published. Advisory prose, not a contract: the wording is deliberately volatile, there are no per-warning codes, and consumers must not branch on the content. The render-time CSP is the enforcement." } }, "required": [ @@ -5662,6 +5731,9 @@ "DocumentsV2CreateBody": { "type": "object", "properties": { + "app": { + "$ref": "#/components/schemas/DocumentsV2CreateApp" + }, "containers": { "$ref": "#/components/schemas/ContainersOnCreate" }, @@ -5721,7 +5793,116 @@ "modelId", "name" ], - "additionalProperties": false + "additionalProperties": false, + "allOf": [ + { + "not": { + "required": [ + "app", + "containers" + ] + } + }, + { + "not": { + "required": [ + "app", + "controls" + ] + } + }, + { + "not": { + "required": [ + "app", + "settings" + ] + } + } + ] + }, + "DocumentsV2CreateApp": { + "type": "object", + "properties": { + "html": { + "type": "string", + "minLength": 1, + "maxLength": 2097152, + "description": "The complete app HTML document, capped at 2 MiB of UTF-8 (maxLength counts characters — the byte cap is what the server enforces). Every write appends an immutable revision." + }, + "settings": { + "$ref": "#/components/schemas/DocumentsV2AppSettings" + } + }, + "required": [ + "html" + ], + "additionalProperties": false, + "description": "App slice of a create (alpha): creates the document as an app document, suppressing the dashboard bootstrap. Same shape as the app PUT body — the complete `html` plus an optional full `settings` replacement (locked-down defaults when omitted). Mutually exclusive with `containers`, `controls`, and `settings`." + }, + "DocumentsV2AppSettings": { + "type": "object", + "properties": { + "allowClipboard": { + "type": "boolean", + "default": false + }, + "allowDefaultMapProviders": { + "type": "boolean", + "default": false + }, + "allowDownloads": { + "type": "boolean", + "default": false + }, + "allowExternalNavigation": { + "type": "boolean", + "default": false + }, + "allowInternalNavigation": { + "type": "boolean", + "default": false + }, + "externalNavOpensInNewTab": { + "type": "boolean", + "default": true + }, + "frameDomains": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "frameDomainsEnabled": { + "type": "boolean", + "default": false + }, + "navAllowedDomains": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "navAllowedDomainsEnabled": { + "type": "boolean", + "default": false + }, + "safeDomains": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "safeDomainsEnabled": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false, + "description": "The app's sandbox settings: capability toggles plus the safe-domain and navigation allowlists. A write replaces the whole object; omitted fields take their locked-down defaults. Host lists are normalized (deduped, invalid or Omni-owned hosts dropped) before they are stored." }, "ContainersOnCreate": { "type": [ @@ -26909,7 +27090,7 @@ "url" ], "additionalProperties": false, - "description": "Present only for app documents. The app's HTML and settings live at the `url` sub-resource; the document body carries nothing app-scoped." + "description": "Present only for app documents, and only while apps are enabled for the organization. The app's HTML and settings live at the `url` sub-resource (`PUT` / `PATCH …/draft/app` or `…/draft/{draftIdentifier}/app`); the document body carries nothing app-scoped." }, "Containers": { "type": "array", @@ -31245,7 +31426,7 @@ } }, "additionalProperties": false, - "description": "Accepted so a GET of an app document round-trips through PATCH; nothing in it is written. A `url` that does not address this document's app is rejected with 409. App content and settings are written only at the `…/app` sub-resource." + "description": "Accepted so a GET of an app document round-trips through PATCH; nothing in it is written. A `url` that does not address this document's app is rejected with 409. App content and settings are written only at the app sub-resource routes (`PUT` / `PATCH …/draft/app` or `…/draft/{draftIdentifier}/app`)." }, "DocumentsV2PatchDraftBody": { "type": "object", @@ -31319,60 +31500,7 @@ "settings" ] }, - "DocumentsV2AppSettings": { - "type": "object", - "properties": { - "allowClipboard": { - "type": "boolean", - "default": false - }, - "allowDefaultMapProviders": { - "type": "boolean", - "default": false - }, - "allowDownloads": { - "type": "boolean", - "default": false - }, - "allowExternalNavigation": { - "type": "boolean", - "default": false - }, - "allowInternalNavigation": { - "type": "boolean", - "default": false - }, - "externalNavOpensInNewTab": { - "type": "boolean", - "default": true - }, - "navAllowedDomains": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "navAllowedDomainsEnabled": { - "type": "boolean", - "default": false - }, - "safeDomains": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "safeDomainsEnabled": { - "type": "boolean", - "default": false - } - }, - "additionalProperties": false, - "description": "The app's sandbox settings: capability toggles plus the safe-domain and navigation allowlists. A write replaces the whole object; omitted fields take their locked-down defaults. Host lists are normalized (deduped, invalid or Omni-owned hosts dropped) before they are stored." - }, - "DocumentsV2PutAppResponse": { + "DocumentsV2AppWriteResponse": { "allOf": [ { "$ref": "#/components/schemas/DocumentsV2PatchDraftResponse" @@ -31388,7 +31516,7 @@ "items": { "type": "string" }, - "description": "Non-blocking warnings — present only when there are any. Currently: external resource hosts the app's iframe CSP will block until an org admin allows them. The write itself succeeded." + "description": "Non-blocking warnings — present only when there are any. Currently: external resource hosts the app's iframe CSP will block until an org admin allows them. The write itself succeeded. Advisory prose, not a contract: the wording is deliberately volatile, there are no per-warning codes, and consumers must not branch on the content. The render-time CSP is the enforcement." } }, "required": [ @@ -31433,6 +31561,66 @@ ], "additionalProperties": false }, + "DocumentsV2PatchAppBody": { + "type": "object", + "properties": { + "htmlEdits": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DocumentsV2HtmlEdit" + }, + "minItems": 1, + "maxItems": 20, + "description": "Targeted edits applied in order against the current HTML, all-or-nothing: a failed edit (not found, ambiguous, or result over the 2 MiB cap) rejects the request with 400 naming the edit, and nothing is applied. At most 20 edits per request — each edit scans the whole document." + }, + "settings": { + "allOf": [ + { + "$ref": "#/components/schemas/DocumentsV2AppSettings" + }, + { + "description": "When present, replaces the app settings; omitted fields take their locked-down defaults. When absent, the current settings are kept." + } + ] + } + }, + "additionalProperties": false, + "anyOf": [ + { + "required": [ + "htmlEdits" + ] + }, + { + "required": [ + "settings" + ] + } + ] + }, + "DocumentsV2HtmlEdit": { + "type": "object", + "properties": { + "replace": { + "type": "string", + "description": "Text that replaces the matched `search` text. May be empty (deletion)." + }, + "replaceAll": { + "type": "boolean", + "description": "Replace every occurrence of `search` instead of requiring a unique match." + }, + "search": { + "type": "string", + "minLength": 1, + "description": "Exact substring of the app's current HTML, copied verbatim with enough surrounding context to be unique (unless `replaceAll`)." + } + }, + "required": [ + "replace", + "search" + ], + "additionalProperties": false + }, "DocumentsV2BindQueryModelBody": { "type": "object", "properties": { @@ -33012,6 +33200,63 @@ "permits" ] }, + "FoldersUpdatePermissionSettingsResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "description": "Whether the permission settings were updated successfully" + } + }, + "required": [ + "success" + ] + }, + "FoldersUpdatePermissionSettingsBody": { + "type": "object", + "properties": { + "organizationAccessBoost": { + "type": "boolean", + "description": "Whether everyone in the organization gets access boost on this folder. Setting it true requires an organization role that grants access, either sent in the same request or already on the folder; otherwise the request is rejected with 400. Rejected with 403 when the organization has AccessBoost turned off, or when a user-scoped key lacks boost provisioning." + }, + "organizationRole": { + "$ref": "#/components/schemas/ValidOrganizationContentRole" + } + }, + "additionalProperties": false, + "minProperties": 1 + }, + "ValidOrganizationContentRole": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": [ + "NO_ACCESS" + ] + }, + { + "type": "string", + "enum": [ + "VIEWER" + ] + }, + { + "type": "string", + "enum": [ + "EDITOR" + ] + }, + { + "type": "string", + "enum": [ + "MANAGER" + ] + } + ] + }, "FoldersAddPermissionsResponse": { "type": "object", "properties": { @@ -36259,6 +36504,25 @@ }, "additionalProperties": false }, + "ModelsGitRotateWebhookSecretResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/ModelsGitGetResponse" + }, + { + "type": "object", + "properties": { + "webhookSecret": { + "type": "string", + "description": "The new webhook secret. Returned only here — read it back later with GET /git?include=webhookSecret." + } + }, + "required": [ + "webhookSecret" + ] + } + ] + }, "ModelsContentValidatorGetResponse": { "type": "object", "properties": { @@ -38388,7 +38652,7 @@ "string", "null" ], - "description": "Database table name if uploaded to database scratch schema" + "description": "Database table name if uploaded to database scratch schema. While a save of an input-column dataset is in flight, the named table may not exist yet; rows for such datasets are managed through their workbook, not this API." }, "model_id": { "type": [ @@ -42081,7 +42345,7 @@ "string", "null" ], - "description": "Warehouse (Snowflake) or HTTP path (Databricks)", + "description": "Warehouse (Snowflake), HTTP path (Databricks), or workgroup (Athena)", "example": "COMPUTE_WH" } }, @@ -42393,7 +42657,7 @@ }, "warehouse": { "type": "string", - "description": "Required for Snowflake (specify the warehouse) and Databricks (specify the HTTP path). May be omitted for Snowflake OAuth connections, in which case each user's Snowflake default warehouse applies.", + "description": "Required for Snowflake (specify the warehouse) and Databricks (specify the HTTP path). May be omitted for Snowflake OAuth connections, in which case each user's Snowflake default warehouse applies. Optional for Athena (specify the workgroup); defaults to \"primary\".", "example": "COMPUTE_WH" }, "wifAudience": { @@ -42871,7 +43135,7 @@ "string", "null" ], - "description": "Warehouse (Snowflake) or HTTP path (Databricks)", + "description": "Warehouse (Snowflake), HTTP path (Databricks), or workgroup (Athena)", "example": "COMPUTE_WH" } }, @@ -43220,7 +43484,7 @@ }, "warehouse": { "type": "string", - "description": "Warehouse (Snowflake) or HTTP path (Databricks)", + "description": "Warehouse (Snowflake), HTTP path (Databricks), or workgroup (Athena)", "example": "COMPUTE_WH" } }, @@ -45419,11 +45683,11 @@ { "schema": { "type": "string", - "description": "Dashboard identifier (short ID or UUID)", + "description": "A published dashboard's identifier, or one of its draft identifiers (short ID or UUID). A draft renders its unpublished content against the branch it was taken on.", "example": "12db1a0a" }, "required": true, - "description": "Dashboard identifier (short ID or UUID)", + "description": "A published dashboard's identifier, or one of its draft identifiers (short ID or UUID). A draft renders its unpublished content against the branch it was taken on.", "name": "identifier", "in": "path" }, @@ -45491,11 +45755,11 @@ { "schema": { "type": "string", - "description": "Dashboard identifier (short ID or UUID)", + "description": "A published dashboard's identifier, or one of its draft identifiers (short ID or UUID). A draft renders its unpublished content against the branch it was taken on.", "example": "12db1a0a" }, "required": true, - "description": "Dashboard identifier (short ID or UUID)", + "description": "A published dashboard's identifier, or one of its draft identifiers (short ID or UUID). A draft renders its unpublished content against the branch it was taken on.", "name": "identifier", "in": "path" }, @@ -45555,11 +45819,11 @@ { "schema": { "type": "string", - "description": "Dashboard identifier (short ID or UUID)", + "description": "A published dashboard's identifier, or one of its draft identifiers (short ID or UUID). A draft renders its unpublished content against the branch it was taken on.", "example": "12db1a0a" }, "required": true, - "description": "Dashboard identifier (short ID or UUID)", + "description": "A published dashboard's identifier, or one of its draft identifiers (short ID or UUID). A draft renders its unpublished content against the branch it was taken on.", "name": "identifier", "in": "path" }, @@ -47177,7 +47441,7 @@ }, "/api/v2/documents": { "post": { - "description": "Create a brand-new document and publish it live. Accepts creation metadata (`modelId`, `name`, optional `identifier` / `description` / `folderId`) plus the same content slice as the PATCH body — `queryPresentations`, `controls`, `settings`, `containers`. The server mints internal tile identifiers, so callers omit `miniUuid`. Tiles in `queryPresentations` are merged by key over the single empty seed tile at key `\"1\"`; write to `\"1\"` (or send it as `null`) to replace the seed.\n\nWhen `containers` is omitted, every dashboard-eligible tile is auto-placed in a default layout. When `containers` is present, it fully defines the layout — tiles it does not reference are stored but not rendered. Send `containers: null` to create a workbook-only document with no dashboard (`controls` and `settings` must then be omitted); an empty `containers: []` is rejected.\n\nThe new document is published live before the response returns. As a first publish of brand-new content it is not subject to the org’s `requirePullRequestToPublish` policy (which gates edits to existing content).", + "description": "Create a brand-new document and publish it live. Accepts creation metadata (`modelId`, `name`, optional `identifier` / `description` / `folderId`) plus the same content slice as the PATCH body — `queryPresentations`, `controls`, `settings`, `containers`. The server mints internal tile identifiers, so callers omit `miniUuid`. Tiles in `queryPresentations` are merged by key over the single empty seed tile at key `\"1\"`; write to `\"1\"` (or send it as `null`) to replace the seed.\n\nWhen `containers` is omitted, every dashboard-eligible tile is auto-placed in a default layout. When `containers` is present, it fully defines the layout — tiles it does not reference are stored but not rendered. Send `containers: null` to create a workbook-only document with no dashboard (`controls` and `settings` must then be omitted); an empty `containers: []` is rejected.\n\nTo create an **app document** in the same single call (alpha, like the app sub-resource routes), send `app: { html, settings? }` — the same shape as the app PUT body, with the same validation (byte cap, strict body) and gates (org app toggle; user-scoped keys need the role’s `allowCreateApps` on the model, or on its parent shared model when `modelId` is a `SHARED_EXTENSION`). `app` suppresses the dashboard bootstrap — the document arrives workbook-only plus the app — so combining it with `containers` (including `null`), `controls`, or `settings` is rejected at the schema (400). Host warnings ride the response `warnings` exactly like the app PUT: writes never reject on host policy.\n\nA document carries at most one of a dashboard or an app — never both; workbook-only is valid. The app HTML and settings live only at the app sub-resource routes; the document read carries an `app` slice pointing here, and the whole-document PATCH accepts that slice back only as it was read.\n\nThe new document is published live before the response returns. As a first publish of brand-new content it is not subject to the org’s `requirePullRequestToPublish` policy (which gates edits to existing content).", "operationId": "documentsV2Create", "summary": "Create document", "tags": [ @@ -47195,7 +47459,7 @@ }, "responses": { "201": { - "description": "Document created and published successfully.", + "description": "Document created and published successfully. For an `app` create, `warnings` names any resource hosts the app’s iframe CSP will block until an org admin allows them.", "content": { "application/json": { "schema": { @@ -47205,16 +47469,16 @@ } }, "400": { - "description": "Invalid request body or schema validation error (e.g. unknown top-level field, name too long, query presentation cap exceeded), or the `identifier` is already in use." + "description": "Invalid request body or schema validation error (e.g. unknown top-level field, name too long, query presentation cap exceeded, `app` combined with a dashboard-scoped field, an empty or oversized `app.html` — the cap is 2 MiB), or the `identifier` is already in use." }, "401": { "description": "Authentication required." }, "403": { - "description": "Insufficient permissions to create a document on this model." + "description": "Insufficient permissions to create a document on this model. For an `app` create, also: apps not enabled for the organization, or (user-scoped keys) the role does not allow creating apps." }, "404": { - "description": "Base model or branch not found." + "description": "Base model not found." }, "405": { "description": "Method not allowed." @@ -47602,7 +47866,82 @@ ], "responses": { "200": { - "description": "The published app content.", + "description": "The published app content.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentsV2AppContentResponse" + } + } + } + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "Insufficient permissions to read the document." + }, + "404": { + "description": "Document not found, the document has no app, or the organization has apps disabled." + }, + "405": { + "description": "Method not allowed." + } + } + } + }, + "/api/v2/documents/{identifier}/draft/{draftIdentifier}/app": { + "get": { + "description": "**Alpha.** The app sub-resource may change shape without a deprecation cycle while apps mature. The document routes are stable.\n\nRead the app content on a named draft — the draft-side counterpart of `GET …/app`, and the read half of the draft write loop: app writes are last-write-wins whole-document replaces, so fetch the draft’s latest HTML here before building the next `PUT …/draft/{draftIdentifier}/app` body. Same response shape as the published read. Writes cap the HTML at 2 MiB — apps saved before the cap may read back larger, and a body over the cap is rejected on the way back in. The published document’s content is unaffected by draft edits — read it via `GET …/app`.\n\nA draft with no app is a 404 — including a draft that carries a dashboard, which can never carry an app.\n\nA document carries at most one of a dashboard or an app — never both; workbook-only is valid. The app HTML and settings live only at the app sub-resource routes; the document read carries an `app` slice pointing here, and the whole-document PATCH accepts that slice back only as it was read.", + "operationId": "documentsV2GetDraftApp", + "summary": "Read app content on a draft", + "tags": [ + "Documents" + ], + "x-experimental": true, + "parameters": [ + { + "schema": { + "type": "string", + "description": "Draft workbook identifier (see `PATCH /api/v2/documents/{identifier}/draft`).", + "example": "def456" + }, + "required": true, + "description": "Draft workbook identifier (see `PATCH /api/v2/documents/{identifier}/draft`).", + "name": "draftIdentifier", + "in": "path" + }, + { + "schema": { + "type": "string", + "description": "Published document identifier.", + "example": "abc123" + }, + "required": true, + "description": "Published document identifier.", + "name": "identifier", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "0", + "1", + "true", + "false" + ], + "description": "Set `true` or `1` to pretty-print (2-space indent) the response; `false` / `0` (the default) is compact. Key ordering is deterministic regardless." + }, + "required": false, + "description": "Set `true` or `1` to pretty-print (2-space indent) the response; `false` / `0` (the default) is compact. Key ordering is deterministic regardless.", + "name": "pretty", + "in": "query" + } + ], + "responses": { + "200": { + "description": "The draft’s app content.", "content": { "application/json": { "schema": { @@ -47618,19 +47957,17 @@ "description": "Insufficient permissions to read the document." }, "404": { - "description": "Document not found, the document has no app, or the organization has apps disabled." + "description": "Document or draft not found, the draft has no app (a dashboard draft never does), or the organization has apps disabled." }, "405": { "description": "Method not allowed." } } - } - }, - "/api/v2/documents/{identifier}/draft/{draftIdentifier}/app": { - "get": { - "description": "**Alpha.** The app sub-resource may change shape without a deprecation cycle while apps mature. The document routes are stable.\n\nRead the app content on a named draft — the draft-side counterpart of `GET …/app`, and the read half of the draft write loop: app writes are last-write-wins whole-document replaces, so fetch the draft’s latest HTML here before building the next `PUT …/draft/{draftIdentifier}/app` body. Same response shape as the published read. Writes cap the HTML at 2 MiB — apps saved before the cap may read back larger, and a body over the cap is rejected on the way back in. The published document’s content is unaffected by draft edits — read it via `GET …/app`.\n\nA draft with no app is a 404 — including a draft that carries a dashboard, which can never carry an app.\n\nA document carries at most one of a dashboard or an app — never both; workbook-only is valid. The app HTML and settings live only at the app sub-resource routes; the document read carries an `app` slice pointing here, and the whole-document PATCH accepts that slice back only as it was read.", - "operationId": "documentsV2GetDraftApp", - "summary": "Read app content on a draft", + }, + "put": { + "description": "**Alpha.** The app sub-resource may change shape without a deprecation cycle while apps mature. The document routes are stable.\n\nReplace the app HTML on an existing draft — creating the app when the draft is workbook-only — optionally replacing the app `settings` in the same call. Operates only on the draft named by `draftIdentifier`, which the caller creates first via `PATCH …/draft`; a published app is never addressable for writing, so every edit is draft-then-publish by construction. No auto-publish — publish via `POST …/draft/publish`.\n\nLast-write-wins, like every app write in the UI: the body is applied as given, with no expected-version precondition. Every write appends an immutable `app_history` revision, so nothing is lost — only the head moves.\n\nWrites never reject on host policy. External `